在python中从套件向TestCase提供参数

在python中从套件向TestCase提供参数,python,unit-testing,testcase,Python,Unit Testing,Testcase,来自python文档(http://docs.python.org/library/unittest.html): 下面是如何调用这些测试用例: def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('test_default_size')) suite.addTest(WidgetTestCase('test_resize')) return suite 是否可以在Widg

来自python文档(http://docs.python.org/library/unittest.html):

下面是如何调用这些测试用例:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('test_default_size'))
    suite.addTest(WidgetTestCase('test_resize'))
    return suite
是否可以在WidgetTestCase中插入参数custom_参数,如:

class WidgetTestCase(unittest.TestCase):
    def setUp(self,custom_parameter):
        self.widget = Widget('The widget')
        self.custom_parameter=custom_parameter

我不这么认为,安装程序的签名需要是unittest所期望的,那么,安装程序在testcase的run方法中自动调用为setUp()。。。除非覆盖所需的var中的run to pass,否则无法传递它。但我认为,您想要的与单元测试的目的背道而驰。不要试图用枯燥的哲学来解释这一点,您测试的每个单元都应该是类的一部分,甚至是函数/方法的一部分

这是我最近一直在想的事情。是的,这是很有可能做到的。我叫它,但我认为参数化可能更准确。我提出了一个概念证明作为要点。简而言之,它是一个元类,允许您定义一个场景并对其运行测试。有了它,您的示例可以是这样的:

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 测试小部件宽度0(\uuuuu main\uuuuu.widget\uu width)。。。好啊 测试小部件宽度1(\uuuuu main\uuuuu.widget\uu width)。。。好啊 ---------------------------------------------------------------------- 在0.001s内运行了2次测试 好啊 如您所见,场景在运行时转换为测试

现在我还不确定这是否是个好主意。我在测试中使用它,在测试中,我有很多以文本为中心的案例,这些案例在稍微不同的数据上重复相同的断言,这有助于我捕获一些小的边缘案例。但这一要点中的课程确实有效,我相信它能实现你的目标

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

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

我已经找到了一种方法来做到这一点,但这有点困难

基本上,我要做的是在TestCase中添加一个
\uuuuuu init\uuuuuu
方法,该方法定义一个“默认”参数和一个
\uuuuuuu str\uuuuu
,以便我们能够区分以下情况:

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
是不需要参数化的测试


例如,我对真实数据进行了一系列测试,需要对每个数据进行一系列测试,但我也有一些合成数据集,用于测试数据中通常不存在的某些边缘情况,我只需要对这些情况进行某些测试,因此,他们在
其他WidgetTestCases
中进行自己的测试,我认为这不是一个好主意。单元测试应该足够彻底,以便在您的情况下测试所有功能,因此不需要传递不同的参数

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

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

  • 彻底-您的单元测试应该有100%的代码覆盖率,并覆盖所有可能的情况。你想用多个站点测试你的代码吗?相反,使用站点可能包含的所有功能测试代码。如果不了解您的应用程序的更多功能,我无法在这一点上提供太多建议

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


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

我所做的就是刚刚添加的测试套件模块

WidgetTestCase.CustomParameter="some_address"

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

我想你必须使用
setattr
custom\u参数包含www地址来测试。我可以为我必须测试的每个站点编写另一个单元测试,但这将是一个代码重复,因为我们使用的是
unittest
框架,并不一定意味着我们在进行单元测试。在我的例子中,为了获得完整的代码覆盖率,我需要十几个输入文件,覆盖不同的用例。我需要在每个函数上测试六个函数,因此我没有编写72个不同的测试,而是编写了一个测试用例类,其中包含6个函数中的每个函数的测试,并多次将其添加到测试套件中,每个测试文件一次,使用字典存储预期结果。它产生了更干净、更简单、更不容易出错的测试代码,不需要为不同的输入数据编写多个测试。这显然是荒谬的。在您的问题中,您从未提到您没有编写单元测试,因此我认为假设您正在使用
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
WidgetTestCase.CustomParameter="some_address"