我有一个看起来像这样的测试用例:
def MyTestCase(unittest.Testcase):
def test_input01(self):
input = read_from_disk('input01')
output = run(input)
validated_output = read_from_disk('output01')
self.assertEquals(output, validated_output)
def test_input02(self):
input = read_from_disk('input02')
# ...
# and so on, for 30 inputs, from input01 to input30
现在,我知道测试代码可能有点重复,因为简单性比简洁性更重要。但是这变得非常容易出错,因为当我决定更改这里使用的某些功能的签名时,我不得不在所有30个地方进行更改。
我可以将它重构为已知输入的循环,但我确实希望每个输入都保持单独的测试,所以我认为我应该使用test_inputxx
方法。
我做错了什么?
答案 0 :(得分:12)
编写辅助函数以从测试用例中删除重复:
def MyTestCase(unittest.Testcase):
def run_input_output(self, suffix):
input = read_from_disk('input'+suffix)
output = run(input)
validated_output = read_from_disk('output'+suffix)
self.assertEquals(output, validated_output)
def test_input01(self): self.run_input_output('01')
def test_input02(self): self.run_input_output('02')
def test_input03(self): self.run_input_output('03')
答案 1 :(得分:2)
我喜欢Ned Batchelder的解决方案。但是对于子孙后代,如果您经常更改输入数量,则可以执行以下操作:
def MyTestCase(unittest.Testcase):
def __init__(self, *args, **kwargs):
for i in range(1,31):
def test(self, suffix=i):
input = read_from_disk('input%02d' % suffix)
output = run(input)
validated_output = read_from_disk('output%02d' % suffix)
self.assertEquals(output, validated_output)
setattr(self, 'test_input%02d' % i) = test
super(MyTestCase, self).__init__(*args, **kwargs)
答案 2 :(得分:1)
这样的事情如何,以便报告输入失败。
def MyTestCase(unittest.Testcase):
def test_input01(self):
for i in range(1,30):
input = read_from_disk('input%.2d' %i)
output = run(input)
validated_output = read_from_disk('output%.2d' %i)
self.assertEquals(output, validated_output, 'failed on test case %.2d' %i)
答案 3 :(得分:1)
我最喜欢的这种测试工具是参数化测试用例,如下所示:
from nose_parameterized import parameterized
class MyTestCase(unittest.TestCase):
@parameterized.expand([(1,), (2,), (3,)])
def test_read_from_disk(self, file_number):
input = read_from_disk('input%02d' % file_number)
expected = read_from_disk('output%02d' % file_number)
actual = run(input)
self.assertEquals(expected, actual)
编写测试用例以获取所需的任何参数,将参数化函数包装在@parameterized.expand
装饰器中,并在expand()调用中提供输入参数集。然后,测试运行器为每组参数运行一个单独的测试!
在这种情况下,只有一个参数,因此expand()
调用具有不幸的额外嵌套级别,但是当您的用例稍微复杂并且您使用时,该模式会变得特别好param
对象为你的测试函数提供args和kwargs:
from nose_parameterized import parameterized, param
class MyTestCase(unittest.TestCase):
@parameterized.expand([
param(english='father', spanish='padre'),
param(english='taco', spanish='taco'),
('earth', 'tierra'), # A regular tuple still works too, but is less readable
...
])
def test_translate_to_spanish(self, english, spanish):
self.assertEqual(translator(english), spanish)
该模式允许您轻松,清晰地指定多组输入参数,并且只编写一次测试逻辑。
我使用nose进行测试,因此我的示例使用nose-parameterized,但还有unittest-compatible version。