基本使用
下载 pip install ddt ,ddt数据驱动只能适用于unittest子类的类装饰器。
import unittest
from ddt import ddt, data, unpack search_data = (‘data1‘, ‘data2‘, ‘data3‘) # 单个参数 @ddt class testDDT(unittest.TestCase): # 单个参数 @data(*search_data) # 传入可序列换对象,进行拆分 def testddt(self, data): print(data) if __name__ == ‘__main__‘: unittest.main()
"""
result:
data1
data2
data3
"""
import unittest from ddt import ddt, data, unpack search_datas = ( [‘data1‘, ‘data2‘, ‘data2‘], [‘code1‘, ‘code2‘, ‘code3‘], ) # 多个参数 @ddt class testDDT(unittest.TestCase): # 多个参数 @data(*search_datas) # data传入可序列化对象,会进行第一次拆分 @unpack # unpack 将data序列化后的值再次判断,如果是列表或者数组,再次拆分 def testddts(self, *args, **kwargs): print(args) print(kwargs) if __name__ == ‘__main__‘: unittest.main()
"""
result:
(‘data1‘, ‘data2‘, ‘data2‘)
{}
(‘code1‘, ‘code2‘, ‘code3‘)
{}
"""
import os import unittest from ddt import ddt, data, unpack, file_data search_datas = ( [‘data1‘, ‘data2‘, ‘data2‘], [‘code1‘, ‘code2‘, ‘code3‘], ) # 多个参数 @ddt class testDDT(unittest.TestCase): # 文件参数化 @file_data(os.path.join(ROOT_DIR, "params", "mysql_mask_all_types.yaml")) # 文件路径 def test_file_data(self, **kwargs): print(kwargs) if __name__ == ‘__main__‘: unittest.main()
源码分析
主要函数和装饰器
def data(*values): """ Method decorator to add to your test methods. Should be added to methods of instances of ``unittest.TestCase``. """ # print("data") # print(values) global index_len index_len = len(str(len(values))) return idata(values)
def idata(iterable):
"""
Method decorator to add to your test methods.
Should be added to methods of instances of ``unittest.TestCase``.
"""
def wrapper(func):
setattr(func, DATA_ATTR, iterable)
return func
return wrapper
def unpack(func): """ Method decorator to add unpack feature. """ setattr(func, UNPACK_ATTR, True) return func
def file_data(value, yaml_loader=None): def wrapper(func): setattr(func, FILE_ATTR, value) if yaml_loader: setattr(func, YAML_LOADER_ATTR, yaml_loader) return func return wrapper
ddt参数化的用例名
ddt默认源码为:支持两种格式如下
@unique class TestNameFormat(Enum): """ An enum to configure how ``mk_test_name()`` to compose a test name. Given the following example: .. code-block:: python @data("a", "b") def testSomething(self, value): ... if using just ``@ddt`` or together with ``DEFAULT``: * ``testSomething_1_a`` * ``testSomething_2_b`` if using ``INDEX_ONLY``: * ``testSomething_1`` * ``testSomething_2`` """ DEFAULT = 0 INDEX_ONLY = 1
ddt 源码修改
原始: def data(*values): """ Method decorator to add to your test methods. Should be added to methods of instances of ``unittest.TestCase``. """ # print("data") # print(values) global index_len index_len = len(str(len(values))) return idata(values) def idata(iterable): """ Method decorator to add to your test methods. Should be added to methods of instances of ``unittest.TestCase``. """ def wrapper(func): setattr(func, DATA_ATTR, iterable) return func return wrapper 修改为: def data(*values, **kwargs): """ Method decorator to add to your test methods. Should be added to methods of instances of ``unittest.TestCase``. """ # print("data") # print(values) global index_len index_len = len(str(len(values))) return idata(values, **kwargs) def idata(iterable, **kwargs): """ Method decorator to add to your test methods. Should be added to methods of instances of ``unittest.TestCase``. """ def wrapper(func): setattr(func, DATA_ATTR, iterable) if kwargs: new = [[i, j] for i, v in kwargs.items() for j in v] setattr(func, DATA_ATTR, new) setattr(func, UNPACK_ATTR, True) return func return wrapper
def mk_test_name(name, value, index=0, name_fmt=TestNameFormat.DEFAULT): # Add zeros before index to keep order index = "{0:0{1}}".format(index + 1, index_len) if name_fmt is TestNameFormat.INDEX_ONLY or not is_trivial(value): return "{0}_{1}".format(name, index) try: if isinstance(value, tuple) or isinstance(value, list): # 添加 value = "_".join([str(i) for i in value]) # 添加 else: # 添加 value = str(value) except UnicodeEncodeError: # fallback for python2 value = value.encode(‘ascii‘, ‘backslashreplace‘) test_name = "{0}_{1}_{2}".format(name, index, value) return re.sub(r‘\W|^(?=\d)‘, ‘_‘, test_name)
def ddt(arg=None, **kwargs): fmt_test_name = kwargs.get("testNameFormat", TestNameFormat.DEFAULT) def wrapper(cls): for name, func in list(cls.__dict__.items()): if hasattr(func, DATA_ATTR) and hasattr(func, FILE_ATTR): file_attr = getattr(func, FILE_ATTR) data_data = getattr(func, DATA_ATTR) process_file_data(cls, name, func, file_attr, data_data) delattr(cls, name) elif hasattr(func, DATA_ATTR): for i, v in enumerate(getattr(func, DATA_ATTR)): test_name = mk_test_name( name, getattr(v, "__name__", v), i, fmt_test_name ) test_data_docstring = _get_test_data_docstring(func, v) if hasattr(func, UNPACK_ATTR): if isinstance(v, tuple) or isinstance(v, list): add_test( cls, test_name, test_data_docstring, func, *v ) else: # unpack dictionary add_test( cls, test_name, test_data_docstring, func, **v ) else: add_test(cls, test_name, test_data_docstring, func, v) # if not hasattr(func, FILE_ATTR): 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 # ``arg`` is the unittest‘s test class when decorating with ``@ddt`` while # it is ``None`` when decorating a test class with ``@ddt(k=v)``. return wrapper(arg) if inspect.isclass(arg) else wrapper
原文:https://www.cnblogs.com/buding-01/p/14292280.html