在python中从Suite中为TestCase提供参数

时间:2011-03-17 08:29:48

标签: python unit-testing testcase

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

答案 0 :(得分:6)

我所做的是刚刚添加的test_suite模块

WidgetTestCase.CustomParameter="some_address"

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

答案 1 :(得分:3)

这是我最近想到的事情。是的,这是非常可能的。我称之为scenario testing,但我认为参数化可能更准确。我把概念证明作为要点here。简而言之,它是一个元类,允许您定义一个场景并对其进行一系列测试。有了它你的例子可以是这样的:

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)

运行时,元类会写出2个单独的测试,因此输出类似于:

$ 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

正如您所看到的,场景在运行时转换为测试。

现在我还不确定这是不是一个好主意。我在测试中使用它,我有很多以文本为中心的情况,在稍微不同的数据上重复相同的断言,这有助于我抓住小边缘情况。但是那个要点中的课程确实有效,我相信它能完成你所追求的目标。

请注意,通过一些技巧,测试用例可以被赋予名称,甚至可以从外部源(如文本文件或数据库)中提取。它没有记录,但在元类中的一些挖掘应该让你开始。我的帖子here还有更多信息和示例。

修改

这是一个丑陋的黑客,我不再支持了。该实现应该作为TestCase的子类完成,而不是作为被黑客攻击的元类。活到老,学到老。更好的解决方案是使用nose generators

答案 2 :(得分:3)

我找到了一种方法来做到这一点,但它有点像淤泥。

基本上,我所做的是在TestCase中添加__init__方法,该方法定义'default'参数和__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 :(得分:0)

我不相信,setUp的签名需要是unittest所期望的,afaik,setUp在testcase的run方法中自动调用为setUp()...你将无法通过除非你重写run以传递你想要的var。但我认为你想要的东西会击败单位测试的目的。不要试图使用DRY哲学,你正在测试的每个单元应该是一个类的一部分,甚至是函数/方法的一部分。

答案 4 :(得分:0)

我不认为这是个好主意。单元测试应该足够彻底,以便您测试案例中的所有功能,因此不需要传递不同的参数。

你提到你正在传递一个www地址 - 这几乎肯定不是一个好主意。如果您尝试在网络连接断开的计算机上运行测试会发生什么?你的测试应该是:

  • 自动 - 它们将在支持您的应用的所有计算机和平台上运行,无需用户干预。他们不应该依赖外部环境来传递。这意味着(除其他事项外)依赖正确建立的互联网连接是一个坏主意。您可以通过提供虚拟数据来解决这个问题。而不是将URL传递给资源,抽象出数据源并传入数据流或其他任何东西。这在python中特别容易,因为你可以使用python的duck-typing来呈现类似流的对象(python经常使用“类似文件”的对象出于这个原因!)。

  • 彻底 - 您的单元测试应具有100%的代码覆盖率,并涵盖所有可能的情况。您想要使用多个站点测试您的代码吗?而是使用站点可能包含的所有可能功能测试您的代码。如果不了解您的应用程序的功能,我就不能提供太多建议。

现在,看起来你的测试将是大量数据驱动的。有许多工具允许您为单元测试定义数据集并在测试中加载它们。例如,查看python测试夹具。

我意识到这不是你正在寻找的答案,但我认为如果你遵循这些原则,从长远来看你会有更多的快乐。