从套件向Python的测试用例传递参数

10 投票
5 回答
8336 浏览
提问于 2025-04-16 13:53

来自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 个回答

3

我找到了一种方法来实现这个,但有点绕。

基本上,我在测试用例(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里有自己的测试。

3

最近我一直在思考这个问题。其实这是完全可以做到的。我称之为场景测试,不过我觉得用“参数化”这个词可能更准确。我在这里放了一个概念验证的示例,可以查看。简单来说,这是一种元类,它允许你定义一个场景,并对这个场景进行多次测试。这样,你的例子可能会像这样:

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生成器

6

我在 test_suite 模块里做的就是添加了这段代码

WidgetTestCase.CustomParameter="some_address"

最简单的解决方案往往是最好的 :)

撰写回答