我有一些测试数据,想为每个项目创建一个单元测试。我的第一个想法是这样做的:

import unittest

l = [["foo", "a", "a",], ["bar", "a", "b"], ["lee", "b", "b"]]

class TestSequence(unittest.TestCase):
    def testsample(self):
        for name, a,b in l:
            print "test", name
            self.assertEqual(a,b)

if __name__ == '__main__':
    unittest.main()

这样做的缺点是它在一个测试中处理所有数据。我想在飞行中为每个项目生成一个测试。有什么建议吗?


当前回答

使用ddt库。它为测试方法添加了简单的装饰器:

import unittest
from ddt import ddt, data
from mycode import larger_than_two

@ddt
class FooTestCase(unittest.TestCase):

    @data(3, 4, 12, 23)
    def test_larger_than_two(self, value):
        self.assertTrue(larger_than_two(value))

    @data(1, -3, 2, 0)
    def test_not_larger_than_two(self, value):
        self.assertFalse(larger_than_two(value))

这个库可以用pip安装。它不需要nose,并且与标准库unittest模块一起出色地工作。

其他回答

这实际上与之前的回答中提到的parameterized相同,但具体到unittest:

def sub_test(param_list):
    """Decorates a test case to run it as a set of subtests."""

    def decorator(f):

        @functools.wraps(f)
        def wrapped(self):
            for param in param_list:
                with self.subTest(**param):
                    f(self, **param)

        return wrapped

    return decorator

使用示例:

class TestStuff(unittest.TestCase):
    @sub_test([
        dict(arg1='a', arg2='b'),
        dict(arg1='x', arg2='y'),
    ])
    def test_stuff(self, arg1, arg2):
        ...

这被称为“参数化”。

有几个工具支持这种方法。例如:

pytest的装饰 参数化

结果代码如下所示:

from parameterized import parameterized

class TestSequence(unittest.TestCase):
    @parameterized.expand([
        ["foo", "a", "a",],
        ["bar", "a", "b"],
        ["lee", "b", "b"],
    ])
    def test_sequence(self, name, a, b):
        self.assertEqual(a,b)

这将生成测试:

test_sequence_0_foo (__main__.TestSequence) ... ok
test_sequence_1_bar (__main__.TestSequence) ... FAIL
test_sequence_2_lee (__main__.TestSequence) ... ok

======================================================================
FAIL: test_sequence_1_bar (__main__.TestSequence)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/parameterized/parameterized.py", line 233, in <lambda>
    standalone_func = lambda *a: func(*(a + p.args), **p.kwargs)
  File "x.py", line 12, in test_sequence
    self.assertEqual(a,b)
AssertionError: 'a' != 'b'

由于历史原因,我将保留大约2008年的原始答案):

我使用的方法是这样的:

import unittest

l = [["foo", "a", "a",], ["bar", "a", "b"], ["lee", "b", "b"]]

class TestSequense(unittest.TestCase):
    pass

def test_generator(a, b):
    def test(self):
        self.assertEqual(a,b)
    return test

if __name__ == '__main__':
    for t in l:
        test_name = 'test_%s' % t[0]
        test = test_generator(t[1], t[2])
        setattr(TestSequense, test_name, test)
    unittest.main()

前几天我在查看radon的源代码时遇到了ParamUnittest (GitHub存储库中的使用示例)。它应该与扩展TestCase的其他框架一起工作(比如Nose)。

这里有一个例子:

import unittest
import paramunittest


@paramunittest.parametrized(
    ('1', '2'),
    #(4, 3),    <---- Uncomment to have a failing test
    ('2', '3'),
    (('4', ), {'b': '5'}),
    ((), {'a': 5, 'b': 6}),
    {'a': 5, 'b': 6},
)
class TestBar(TestCase):
    def setParameters(self, a, b):
        self.a = a
        self.b = b

    def testLess(self):
        self.assertLess(self.a, self.b)
import unittest

def generator(test_class, a, b,c,d,name):
    def test(self):
        print('Testexecution=',name)
        print('a=',a)
        print('b=',b)
        print('c=',c)
        print('d=',d)

    return test

def add_test_methods(test_class):
    test_list = [[3,3,5,6, 'one'], [5,5,8,9, 'two'], [0,0,5,6, 'three'],[0,0,2,3,'Four']]
    for case in test_list:
        print('case=',case[0], case[1],case[2],case[3],case[4])
        test = generator(test_class, case[0], case[1],case[2],case[3],case[4])
        setattr(test_class, "test_%s" % case[4], test)


class TestAuto(unittest.TestCase):
    def setUp(self):
        print ('Setup')
        pass

    def tearDown(self):
        print ('TearDown')
        pass

add_test_methods(TestAuto)

if __name__ == '__main__':
    unittest.main(verbosity=1)

还有一个假说,它增加了模糊或基于属性的测试。

这是一种非常强大的测试方法。