从Python套件向TestCase传递参数

9

来自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

?


我认为你需要使用 setattr - Srinivas Reddy Thatiparthy
custom_parameter包含用于测试的网址。我可以为每个需要测试的网站编写另一个单元测试,但这将导致代码重复。 - user278618
5个回答

6
我所做的是在test_suite模块中添加了。
WidgetTestCase.CustomParameter="some_address"

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

3

最近我一直在思考这个问题。是的,这是完全可能的。我称之为场景测试,但我认为参数化可能更准确。我在gist上放了一个概念证明这里。简而言之,这是一个元类,允许您定义场景并反复运行测试。有了它,您的示例可以像这样:

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
正如您所看到的,场景在运行时被转换为测试用例。
现在我还不确定这是否是一个好主意。我在测试中使用它,其中有很多以文本为中心的案例,这些案例重复了对稍微不同的数据进行相同的断言,这有助于我捕捉一些小边缘情况。但是那个代码片段中的类确实有效,我相信它可以完成您想要的任务。
请注意,通过一些诡计,测试用例可以被赋予名称,甚至可以从外部源(如文本文件或数据库)中提取。它尚未记录,但在元类中进行一些挖掘应该可以让您开始。我的帖子here上还有更多信息和示例。

这是一个丑陋的hack,我不再支持。实现应该是作为TestCase的子类,而不是作为一个hack的元类。活着就要学习。更好的解决方案是使用nose generators


3
我找到了一种方法来做到这一点,但是它有些笨拙。
基本上,我所做的是在测试用例中添加一个__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中有自己的测试。


0

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

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

  • 自动化 - 它们将在支持您的应用程序的所有机器和平台上运行,无需用户干预。它们不应该依赖于外部环境来传递。这意味着(除其他事项外),依赖于正确设置的互联网连接是一个坏主意。您可以通过提供虚拟数据来解决此问题。抽象数据源而不是传递到资源的URL,传递数据流或其他内容。在Python中特别容易,因为您可以利用Python的鸭子类型来呈现类似流的对象(出于这个原因,Python经常使用“类似文件”的对象!)。

  • 彻底 - 您的单元测试应该具有100%的代码覆盖率,并涵盖所有可能的情况。您想要使用多个站点测试您的代码吗?相反,请使用所有可能包含在站点中的功能来测试您的代码。如果不了解更多关于您的应用程序的信息,我无法在此方面提供太多建议。

现在,看起来您的测试将大量使用数据驱动。有许多工具可让您为单元测试定义数据集并在测试中加载它们。例如,请查看Python测试固件。

我知道这不是你想要的答案,但我认为如果你遵循这些原则,长远来看会更有收获。


1
仅仅因为我们使用了unittest框架,并不意味着我们一定在进行单元测试。它也可以用于进行回归测试。 - Mark Booth
在我的情况下,为了获得完整的代码覆盖率,我需要一打输入文件,涵盖不同的使用案例。我需要在每个文件中测试六个函数,因此我没有编写72个不同的测试,而是编写了一个单独的 TestCase 类,其中包含针对每个函数的测试,并将其多次添加到测试套件中,每个测试文件添加一次,使用字典存储预期结果。这导致了更清洁、更简单的测试代码,减少了错误的可能性。 - Mark Booth
没有必要为不同的输入数据编写多个测试。那显然是荒谬的。在你的问题中,你从未提到你没有编写单元测试,所以我认为假设如果你正在使用unittest库,你会编写单元测试是合理的! - Thomi

0

我不这么认为,setUp的签名需要符合unittest的期望,据我所知,setUp会在测试用例的run方法中自动调用setUp()...除非你重写run方法并传入想要的变量,否则你无法通过它。但我认为你想要的东西违背了单元测试的目的。不要试图在这里使用DRY原则,你正在测试的每个单元都应该是一个类的一部分,甚至是一个函数/方法的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接