ddt原理讲解
| import unittest |
| import ddt |
| @ddt.ddt |
| class TestCase(unittest.TestCase): |
| @ddt.data((1, 2), (3, 4)) |
| def test05(self, a): |
| print(a) |
| DATA_ATTR = '%values' |
| FILE_ATTR = '%file_path' |
| YAML_LOADER_ATTR = '%yaml_loader' |
| INDEX_LEN = '%index_len' |
| |
| def data(*values): |
| |
| return idata(values) |
| |
| def idata(iterable, index_len=None): |
| |
| if index_len is None: |
| |
| iterable = tuple(iterable) |
| index_len = len(str(len(iterable))) |
| |
| def wrapper(func): |
| setattr(func, DATA_ATTR, iterable) |
| setattr(func, INDEX_LEN, index_len) |
| return func |
| |
| return wrapper |
| |
| def ddt(arg=None, **kwargs): |
| print("传入的arg参数:",arg) |
| fmt_test_name = kwargs.get("testNameFormat", TestNameFormat.DEFAULT) |
| |
| def wrapper(cls): |
| for name, func in list(cls.__dict__.items()): |
| if hasattr(func, DATA_ATTR): |
| index_len = getattr(func, INDEX_LEN) |
| for i, v in enumerate(getattr(func, DATA_ATTR)): |
| test_name = mk_test_name( |
| name, |
| getattr(v, "__name__", v), |
| i, |
| index_len, |
| fmt_test_name |
| ) |
| test_data_docstring = _get_test_data_docstring(func, v) |
| if hasattr(func, UNPACK_ATTR):没有装饰@ddt.unpack,就过 |
| if isinstance(v, tuple) or isinstance(v, list): |
| add_test( |
| cls, |
| test_name, |
| test_data_docstring, |
| func, |
| *v |
| ) |
| else: |
| |
| add_test( |
| cls, |
| test_name, |
| test_data_docstring, |
| func, |
| **v |
| ) |
| else: |
| add_test(cls, test_name, test_data_docstring, func, v) |
| delattr(cls, name) |
| elif hasattr(func, FILE_ATTR): |
| file_attr = getattr(func, FILE_ATTR) |
| process_file_data(cls, name, func, file_attr) |
| delattr(cls, name) |
| return cls |
| |
| return wrapper(arg) if inspect.isclass(arg) else wrapper |
| def add_test(cls, test_name, test_docstring, func, *args, **kwargs): |
| """ |
| Add a test case to this class. |
| |
| |
| """ |
| setattr(cls, test_name, feed_data(func, test_name, test_docstring, |
| *args, **kwargs)) |
| def feed_data(func, new_name, test_data_docstring, *args, **kwargs): |
| @wraps(func) |
| def wrapper(self): |
| return func(self, *args, **kwargs) |
| wrapper.__name__ = new_name |
| wrapper.__wrapped__ = func |
| |
| if test_data_docstring is not None: |
| wrapper.__doc__ = test_data_docstring |
| else: |
| |
| if func.__doc__: |
| try: |
| wrapper.__doc__ = func.__doc__.format(*args, **kwargs) |
| except (IndexError, KeyError): |
| |
| |
| |
| |
| pass |
| return wrapper |
| |
总结
@ddt.data(参数)
| |
| |
| |
| setattr(func, DATA_ATTR, iterable) |
| setattr(func, INDEX_LEN, index_len) |
给被装饰@ddt.unpack,给函数参数增加解包属性
| # 参数为1,2,3这种,DATA_ATTR为(1,2,3) 遍历后1,2,3都是int类型,不可装饰uppack |
| # 参数为(1,2,3),DATA_ATTR为((1,2,3),)遍历后(1,2,3)可解包,被装饰函数接受3个参数,可以用*args接受 |
| # 参数为(1,2),(3,4)DATA_ATTR为((1,2),(3,4)) 遍历后(1,2),(3,4)可解包,被装饰函数接受2个参数,可以用*args |
| # 参数为 {"name":"xiaowang","age":18},{"name":"xiaowang","age":18}遍历后 {"name":"xiaowang","age":18},{"name":"xiaowang","age":18}可解包,被装饰函数接受2个参数,可以用**kwargs |
| setattr(func, DATA_ATTR, iterable) |
| setattr(func, INDEX_LEN, index_len) |
给类装饰@ddt.ddt,给函数参数增加解包属性
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?