unittest-ddt原理讲解

ddt原理讲解

import unittest
import ddt
@ddt.ddt # 5.装饰整个类
class TestCase(unittest.TestCase):  
    @ddt.data((1, 2), (3, 4)) #1.执行data方法,返回值装饰test05
    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)   # 2.调用idata 此时values是个元祖((1, 2), (3, 4))

def idata(iterable, index_len=None):

    if index_len is None:
        # Avoid consuming a one-time-use generator.
        iterable = tuple(iterable)  #((1, 2), (3, 4))
        index_len = len(str(len(iterable))) #2

    def wrapper(func):  #4.调用 ,此时func指向test05,并为test05对象增加两属性
        setattr(func, DATA_ATTR, iterable) 
        setattr(func, INDEX_LEN, index_len)
        return func

    return wrapper # 3.相当于warpper 装饰test05

def ddt(arg=None, **kwargs): #
    print("传入的arg参数:",arg) # 6.arg指向类名TestCase
    fmt_test_name = kwargs.get("testNameFormat", TestNameFormat.DEFAULT)

    def wrapper(cls):
        for name, func in list(cls.__dict__.items()):# 遍历类都属性
            if hasattr(func, DATA_ATTR): # 被data装饰过的方法都有DATA_ATTR,且值为data的值元祖((1, 2), (3, 4))
                index_len = getattr(func, INDEX_LEN) #2
                for i, v in enumerate(getattr(func, DATA_ATTR)):
                    test_name = mk_test_name(
                        name, # 函数名
                        getattr(v, "__name__", v), #遍历data里面的值
                        i, #0
                        index_len, #1
                        fmt_test_name # 0
                    )  # 改变测试用例的名字
                    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:
                            # unpack dictionary
                            add_test(
                                cls,
                                test_name,
                                test_data_docstring,
                                func,
                                **v
                            )
                    else:
                        add_test(cls, test_name, test_data_docstring, func, v) # 依次遍历传入(1,2),(3,4),所以测试用例只用一个参数接受
                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 # 调用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)) # 给类新命名的函数名字增加返回值属性,返回值指向下面的wrapper
def feed_data(func, new_name, test_data_docstring, *args, **kwargs):
    @wraps(func)
    def wrapper(self):
        return func(self, *args, **kwargs) # func被装饰函数test05的引用
    wrapper.__name__ = new_name
    wrapper.__wrapped__ = func
    # set docstring if exists
    if test_data_docstring is not None:
        wrapper.__doc__ = test_data_docstring
    else:
        # Try to call format on the docstring
        if func.__doc__:
            try:
                wrapper.__doc__ = func.__doc__.format(*args, **kwargs)
            except (IndexError, KeyError):
                # Maybe the user has added some of the formating strings
                # unintentionally in the docstring. Do not raise an exception
                # as it could be that user is not aware of the
                # formating feature.
                pass
    return wrapper


总结

@ddt.data(参数)

  • 1.给被装饰ddt.data增加两个属性
# 参数为1,2,3这种,DATA_ATTR为(1,2,3)INDEX_LEN为3
# 参数为(1,2,3),DATA_ATTR为((1,2,3),)INDEX_LEN为1
# 参数为(1,2),(3,4)DATA_ATTR为((1,2),(3,4)) INDEX_LEN为2
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,给函数参数增加解包属性

  • 执行上面的具体逻辑
posted @ 2022-07-21 22:27  我是小菜鸡丫丫  阅读(181)  评论(0编辑  收藏  举报