我正在尝试测试标准输入(使用raw_input()读取并以简单的打印方式编写)的Python脚本(2.7),但是我找不到该怎么做的方法,并且我确定这个问题是很简单。
这是我脚本的非常非常恢复的代码:
def example():
number = raw_input()
print number
if __name__ == '__main__':
example()
我想编写一个单元测试来检查这一点,但是我找不到如何做。我正在尝试使用StringIO和其他方法,但是我找不到真正做到这一点的解决方案。
有人有主意吗?
PD:当然,在真实的脚本中,我使用具有几行和其他类型数据的数据块。
太感谢了。
编辑:
非常感谢您提供的第一个真正具体的答案,它非常完美,仅在导入时遇到了一些问题StringIO
,因为我正在执行导入StringIO,并且需要进行类似的导入from StringIO import StringIO
(我不太清楚为什么),但是它可能会起作用。
但是我用这种方法发现了另一个问题,在我的项目中,我需要用这种方法测试脚本(在您的支持下,脚本可以很好地工作),但是我想这样做:我有一个传递了很多测试的文件一个脚本,所以我打开文件并读取带有结果块的信息块,并且我想这样做,代码将能够处理检查其结果的块并与其他对象进行同样的操作...
像这样的东西:
class Test(unittest.TestCase):
...
#open file and process saving data like datablocks and results
...
allTest = True
for test in tests:
stub_stdin(self, test.dataBlock)
stub_stdouts(self)
runScrip()
if sys.stdout.getvalue() != test.expectResult:
allTest = False
self.assertEqual(allTest, True)
我知道也许单元测试现在没有意义,但是您可以对我想要的做个想法。因此,这种方式失败了,我也不知道为什么。
典型的技术包括模拟标准sys.stdin
和sys.stdout
所需的项目。如果您不关心Python 3的兼容性,则可以使用该StringIO
模块,但是,如果您想向前思考并愿意限制于Python 2.7和3.3+,则可以在不太多的情况下以这种方式同时支持Python 2和3在io
模块中工作(但需要进行一些修改,但是暂时搁置此想法)。
假设您已经准备好了unittest.TestCase
,可以创建一个实用程序函数(或同一类中的方法),该函数将替换sys.stdin
/,sys.stdout
如下所述。首先进口:
import sys
import io
import unittest
在我最近的一个项目中,我已经为stdin做到了这一点,在stdin中,str
用户(或通过管道的另一个程序)将以stdin的形式输入到您的输入中:
def stub_stdin(testcase_inst, inputs):
stdin = sys.stdin
def cleanup():
sys.stdin = stdin
testcase_inst.addCleanup(cleanup)
sys.stdin = StringIO(inputs)
至于stdout和stderr:
def stub_stdouts(testcase_inst):
stderr = sys.stderr
stdout = sys.stdout
def cleanup():
sys.stderr = stderr
sys.stdout = stdout
testcase_inst.addCleanup(cleanup)
sys.stderr = StringIO()
sys.stdout = StringIO()
请注意,在这两种情况下,它都接受一个测试用例实例,并调用其addCleanup
方法,该方法添加了cleanup
函数调用,该函数调用将把它们重置为结束测试方法持续时间时的位置。效果是,从在测试用例中调用它到结束为止的整个过程中,sys.stdout
都将用该io.StringIO
版本替换朋友,这意味着您可以轻松地检查其值,而不必担心会乱成一团。
最好以这个为例。要使用此功能,您可以简单地创建一个测试用例,如下所示:
class ExampleTestCase(unittest.TestCase):
def test_example(self):
stub_stdin(self, '42')
stub_stdouts(self)
example()
self.assertEqual(sys.stdout.getvalue(), '42\n')
现在,在Python 2中,只有当StringIO
类来自StringIO
模块时,此测试才能通过,而在Python 3中,则不存在这样的模块。您可以做的是使用io
模块中的版本,并对其进行修改,使其在接受的输入方面稍微宽松一些,以便Unicode编码/解码将自动完成,而不是触发异常(例如print
Python中的语句)如果没有以下内容,则2不能很好地工作)。我通常这样做是为了在Python 2和3之间实现交叉兼容性。
class StringIO(io.StringIO):
"""
A "safely" wrapped version
"""
def __init__(self, value=''):
value = value.encode('utf8', 'backslashreplace').decode('utf8')
io.StringIO.__init__(self, value)
def write(self, msg):
io.StringIO.write(self, msg.encode(
'utf8', 'backslashreplace').decode('utf8'))
现在,将示例函数以及此答案中的每个代码片段插入一个文件,您将获得可在Python 2和3中运行的自包含单元测试(尽管您需要print
在Python 3中作为函数调用)以针对stdio进行测试。
还有一点要注意:如果每个测试方法都需要,则可以始终将stub_
函数调用放在setUp
方法中TestCase
。
当然,如果要使用各种与模拟相关的库来存根stdin / stdout,则可以随意这样做,但是如果这是您的目标,则这种方式不依赖任何外部依赖项。
对于第二个问题,必须以某种方式编写测试用例,这些测试用例必须封装在一个方法中,而不是在类级别,否则原始示例将失败。但是,您可能想要执行以下操作:
class Test(unittest.TestCase):
def helper(self, data, answer, runner):
stub_stdin(self, data)
stub_stdouts(self)
runner()
self.assertEqual(sys.stdout.getvalue(), answer)
self.doCleanups() # optional, see comments below
def test_various_inputs(self):
data_and_answers = [
('hello', 'HELLOhello'),
('goodbye', 'GOODBYEgoodbye'),
]
runScript = upperlower # the function I want to test
for data, answer in data_and_answers:
self.helper(data, answer, runScript)
您可能要调用的原因doCleanups
是为了防止清理堆栈变得像所有data_and_answers
对一样深,但这会从清理堆栈中弹出所有内容,因此如果最后还有其他需要清理的内容这可能最终会带来问题-您可以随意离开那里,因为所有与stdio相关的对象都将以相同顺序还原到最后,因此真正的对象将始终在那里。现在我要测试的功能:
def upperlower():
raw = raw_input()
print (raw.upper() + raw),
因此,是的,我所做的一些解释可能会有所帮助:请记住,在一个TestCase
类中,该框架严格依赖实例assertEqual
和其朋友来使其起作用。因此,为了确保在正确的级别上进行测试,您真的想一直调用这些断言,以便在发生错误时输入/答案显示不正确的错误消息会显示出有用的错误消息,而不是正确地显示这些错误消息。直到最后,就像您对for循环所做的一样(这将告诉您出了点问题,但不完全是数百个错误中的哪一个,现在您很生气)。也是helper
方法-您可以将其命名为任意名称,只要它不以test
因为该框架将尝试将其作为一个整体运行,并且将严重失败。因此,只要遵循此约定,您就可以在测试用例中基本包含模板以运行测试-然后可以像我一样在带有大量输入/输出的循环中使用它。
至于您的其他问题:
只是我在导入StringIO时遇到了一些问题,因为我正在执行导入StringIO,并且需要从StringIO导入,就像从StringIO导入StringIO(我真的不明白为什么),但事实上,它确实可行。
好吧,如果您看一下我的原始代码,我确实向您展示了如何做import io
,然后io.StringIO
通过定义覆盖了该类class StringIO(io.StringIO)
。但是它对您有用,因为您严格地从Python 2来执行此操作,而考虑到不到5年将不支持Python 2,我会尽一切可能将针对Python 3的答案作为目标。想想将来可能正在阅读此帖子的与您有类似问题的用户。无论如何,是的,原始的from StringIO import StringIO
作品,因为那StringIO
是StringIO
模块中的类。虽然from cStringIO import StringIO
应该可以导入模块的C
版本StringIO
。之所以起作用,是因为它们都提供了足够接近的接口,因此它们基本上可以按预期工作(直到您尝试在Python 3下运行它)。
再次,将所有这些与我的代码放在一起应该会产生一个自包含的工作测试脚本。请记住要阅读文档并遵循代码的形式,而不要发明自己的语法并希望事情能够起作用(以及确切地说,为什么您的代码不起作用,因为“测试”代码是在类所在的位置定义的)正在构建中,因此所有这些操作都是在Python导入模块时执行的,并且由于测试运行所需的所有东西甚至都不可用(即,类本身甚至还不存在),因此整个过程只是因抽搐而死)。我想,即使您面临的问题确实很普遍,但在这里问问题也有帮助,没有一个快速简单的名称来搜索您的确切问题确实使您很难弄清楚哪里出了问题。:)无论如何,祝您好运,并且对您努力测试代码也有好处。
还有其他方法,但是鉴于我在SO上查看的其他问题/答案似乎无济于事,所以我希望这一方法能解决这一问题。其他参考:
当然,它剥开重复这一切可以用做unittest.mock
Python中可供3.3+或PyPI上的原/滚动反向移植版本,但考虑到这些库隐藏了一些什么实际发生的错综复杂的,他们可能会隐藏一些的有关实际发生(或需要发生)或重定向实际发生方式的详细信息。如果需要,您可以继续阅读unittest.mock.patch
并略微进入StringIO
补丁sys.stdout
部分。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句