如何模拟和测试装饰器?

How to mock and test decorator?

如何测试以下调用第三方库的装饰器?

import third_party_lib
import functools

class MyDecorator:
    def __init__(self, ....):
        self.third_party_lib = ThirdPartyLib(....) # will create a 3rd party instance

    def __call__(self, ...):
        def decorator(f):
            @functools.wraps(f)
            def wrap(*arg, **kwargs):
                result = f(*arg, **kwargs)
                # ....
                a = self.third_party_lib.send(value=result).get()
                # ....
                return result
            return wrap
        return decorator

我需要创建一个单元测试来断言 third_party_lib.send() 如果装饰器装饰了一个函数,就会调用它。理想情况下,还要确保将测试函数的结果传递给函数。

decorator = MyDecorator(....)

@decorator(....)
def test_func():
    ret = ...
    return ret # ret should be passed to `third_party_lib.send()`

如果你想验证第三方函数是否被正确调用,你可以模拟它并检查是否使用正确的参数调用了模拟。由于 ThirdPartyLib 初始化也应被模拟,如评论中所述,您必须确保在设置模拟后构造 docorator,例如通过在测试中构造它:

from unittest import mock

@mock.patch('third_party_lib.ThirdPartyLib')
def test_my_decorator(mocked_lib):
    decorator = MyDecorator()

    @decorator()
    def example_func():
        return 42

    example_func()
    mocked_lib.return_value.send.assert_called_once_with(value=42)

如果你在更多的测试中需要装饰函数,你可以将它包装在一个函数中:

def decorated_func():
    decorator = MyDecorator()

    @decorator()
    def example_func():
        return 42

    return example_func

@mock.patch('third_party_lib.ThirdPartyLib')
def test_my_decorator(mocked_lib):
    decorated_func()()
    mocked_lib.return_value.send.assert_called_once_with(value=42)