问题描述
我一直在使用 DDT 来参数化我的测试,并取得了巨大成功过去几个月.我现在的问题是我似乎无法注入列表变量作为我的数据源.这样做似乎会混淆 DDT,导致它无法参数化我的测试.我开始创建自己的解决方案,但我似乎无法弄清楚最后一部分.
这是迄今为止我所拥有的装饰器 -
def data(*values): def aaa(func): def wrapper(self, *args, **kwargs): pass # return func(self, *args, **kwargs) wrapper.func_name = func.__name__ + 't' wrapper.values = values return wrapper return aaa def c(cls): for name, method in list(cls.__dict__.items()): if hasattr(method, 'values'): for ticket in method.values[0]: test_name = mk_test_name(method.func_name, ticket) print(test_name) setattr(cls, test_name, method(cls, ticket)) return cls
我就这样使用它 -
@c class IntegrationTests(APITestCase): tickets = [1, 2, 3, 4] @data(tickets) def tes(self, t): print(t)
如何让 Python 测试框架识别出我是通过装饰器添加的?我知道这些方法已被添加,因为在 PDB 中发出 dir 命令会显示它们.这样做的目标是我会复制我为列表中的每个项目装饰的测试.对于那些想知道为什么 wrapper() 没有代码的人,我这样做是因为取消注释返回调用的行会导致我装饰的方法在没有参数的情况下执行,从而导致错误.
在我的示例中,我希望执行 4 个不同名称的测试.
解决方案
最好的解决方案是使用python 3.4中unittest的子测试功能.here 找到的文档如下:
class NumbersTest(unittest.TestCase): def test_even(self): """ Test that numbers between 0 and 5 are all even. """ for i in range(0, 6): with self.subTest(i=i): self.assertEqual(i % 2, 0)
对于不能使用python 3.4的人,以下是穷人的替代品.
class sub_test_data(object): def __init__(self, *test_data): self.test_data = test_data def __call__(self, func): func.sub_test_data = self.test_data func.has_sub_tests = True return func def create_test_driver(func, *args): def test_driver(self): try: func(self, *args) except AssertionError as e: e.args += ({"test_args": args},) raise return test_driver def create_sub_tests(cls): for attr_name, func in list(vars(cls).items()): if getattr(func, "has_sub_tests", False): for i, value in enumerate(func.sub_test_data): test_name = 'test_{}_subtest{}'.format(attr_name, i) setattr(cls, test_name, create_test_driver(func, value)) return cls @create_sub_tests class NumbersTest(unittest.TestCase): tickets = [0, 1, 2, 3, 4, 5] @sub_test_data(*tickets) def even(self, t): self.assertEqual(t % 2, 0)