从套件向Python的测试用例传递参数
来自Python文档(http://docs.python.org/library/unittest.html):
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
self.widget = None
def test_default_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
下面是如何调用这些测试用例的方法:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_size'))
suite.addTest(WidgetTestCase('test_resize'))
return suite
有没有可能像这样把参数custom_parameter插入到WidgetTestCase中:
class WidgetTestCase(unittest.TestCase):
def setUp(self,custom_parameter):
self.widget = Widget('The widget')
self.custom_parameter=custom_parameter
?
5 个回答
我找到了一种方法来实现这个,但有点绕。
基本上,我在测试用例(TestCase)里添加了一个__init__
方法,这个方法定义了一个“默认”参数,还有一个__str__
方法,这样我们就能区分不同的测试情况:
class WidgetTestCase(unittest.TestCase):
def __init__(self, methodName='runTest'):
self.parameter = default_parameter
unittest.TestCase.__init__(self, methodName)
def __str__(self):
''' Override this so that we know which instance it is '''
return "%s(%s) (%s)" % (self._testMethodName, self.currentTest, unittest._strclass(self.__class__))
然后在suite()
方法里,我会遍历我的测试参数,把默认参数替换成每个测试特定的参数:
def suite():
suite = unittest.TestSuite()
for test_parameter in test_parameters:
loadedtests = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
for t in loadedtests:
t.parameter = test_parameter
suite.addTests(loadedtests)
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(OtherWidgetTestCases))
return suite
其中OtherWidgetTestCases
是一些不需要参数化的测试。
举个例子,我有一堆针对真实数据的测试,需要对每个数据应用一系列测试,但我也有一些合成数据集,专门用来测试一些在真实数据中通常不存在的边缘情况,而这些合成数据只需要应用特定的测试,所以它们在OtherWidgetTestCases
里有自己的测试。
最近我一直在思考这个问题。其实这是完全可以做到的。我称之为场景测试,不过我觉得用“参数化”这个词可能更准确。我在这里放了一个概念验证的示例,可以查看。简单来说,这是一种元类,它允许你定义一个场景,并对这个场景进行多次测试。这样,你的例子可能会像这样:
class WidgetTestCase(unittest.TestCase):
__metaclass__ = ScenarioMeta
class widget_width(ScenerioTest):
scenarios = [
dict(widget_in=Widget("One Way"), expected_tuple=(50, 50)),
dict(widget_in=Widget("Another Way"), expected_tuple=(100, 150))
]
def __test__(self, widget_in, expected_tuple):
self.assertEqual(widget_in.size, expected_tuple)
当运行时,这个元类会生成两个独立的测试,所以输出会像这样:
$ python myscerariotest.py -v test_widget_width_0 (__main__.widget_width) ... ok test_widget_width_1 (__main__.widget_width) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
正如你所看到的,场景在运行时被转换成了测试。
不过我现在还不确定这是否真的是个好主意。我在测试中使用它,主要是因为我有很多文本相关的案例,它们在稍微不同的数据上重复相同的断言,这样可以帮助我捕捉到一些边缘情况。但是在那个示例中的类确实可以工作,我相信它能实现你想要的效果。
需要注意的是,通过一些技巧,测试用例可以被命名,甚至可以从外部来源获取,比如文本文件或数据库。虽然目前没有文档说明,但在元类中稍微挖掘一下就能让你入门。我的帖子中还有更多信息和示例,可以查看这里。
编辑
这是一个我现在不再支持的丑陋黑客方法。这个实现应该作为TestCase的子类来做,而不是作为一个被修改的元类。活到老学到老。一个更好的解决方案是使用nose生成器。
我在 test_suite 模块里做的就是添加了这段代码
WidgetTestCase.CustomParameter="some_address"
最简单的解决方案往往是最好的 :)