what will happen if i decorate a method with two decorator in python3

Viewed 30

First of all, I was trying to build a test automation project in which cases were managed by built-in unittest in Python 3. Since unittest has no dependency support (as far as I know), I wrote my own decorator to support this:

def depends_on(case=''):
    """dependcy decorator"""
    if not case:
        raise DependencyError
    _mark = []

    def wrap_func(func):
        @wraps(func)
        def inner_func(self):
            # check recursive
            if case == func.__name__:
                raise DependencyError(1)
            _r = self._outcome.result
            _f, _e, _s = _r.failures, _r.errors, _r.skipped

            # check result before
            if not (_f or _e or _s):
                return func(self)
            if _f:
                _mark.extend([fail[0] for fail in _f])
            if _e:
                _mark.extend([error[0] for error in _e])
            if _s:
                _mark.extend([skip[0] for skip in _s])

            unittest.skipIf(
                case in str(_mark),
                f'The pre-depend case :{case} has failed! Skip the specified case!'
            )(func)(self)
        return inner_func
    return wrap_func

and this decorator works fine after testing.

After these work, I want to parameterize my test_method in TestCase; so I wrote other decorators to support it like this:

def ddt(cls):
    """
    :param cls: class which inherit from TestCase
    :return:
    """
    for name, func in list(cls.__dict__.items()):
        if hasattr(func, "PARAMS"):
            for index, case_data in enumerate(getattr(func, "PARAMS")):
                new_test_name = _create_test_name(index, name)
                if isinstance(case_data, dict) and case_data.get("title"):
                    test_desc = str(case_data.get("title"))
                elif isinstance(case_data, dict) and case_data.get("desc"):
                    test_desc = str(case_data.get("desc"))
                elif (not isinstance(case_data, str)) and hasattr(case_data, 'title'):
                    test_desc = str(case_data.title)
                else:
                    test_desc = func.__doc__
                # _update_func will change func's name by add postfix(which is number index)
                func2 = _update_func(new_test_name, case_data, test_desc, func)  
                setattr(cls, new_test_name, func2)
            else:
                delattr(cls, name)
    return cls

def list_data(datas):
    """
    :param datas: testing data set
    :return:
    """

    def wrapper(func):
        setattr(func, "PARAMS", datas)
        return func

    return wrapper

In this code block, I defined two methods: list_data will attach data to test func in TestCase, and ddt will check if a method in the class has the attribute PARAMS, and change its name by adding a numeric index. For example: if the method name is test_demo, it will produce test_demo01 and test_demo02, and will delete test_demo after all of this.

The problem is that sometimes, a parameterized method needs to depend on another method, which will make me use two decorators like this:

@ddt
class TestMyCase(unittest.TestCase)

    @depends_on('test_add_protocol')
    @list_data([
        {'a': 1},
        {'b': 2}
    ])
    def test_delete_protocol(self, data):
       ...

After doing this, it will happen that depends_on stops working. I have realized that the inner decorator will be called first (which means test_delete_protocol will be deleted before depends_on works), and I think that's why it doesn't behave as I expected.

How can I fix it? I have tried to make depends_on inner, but that also doesn't work.

0 Answers
Related