Python Web

Pytest - 如何mock一个装饰器decorator

2021-05-09  本文已影响0人  红薯爱帅

1. 概述

Python项目的自动化单元测试,可以通过Tox和Pytest完成,提高Python项目的可维护性和健壮性。

关于Tox和Pytest的使用方法,可以参考我之前几篇文章:

本文重点介绍一下通过pytest如何mock一个decorator,应用场景比如有登录权限限制的API函数有无限retry的函数,等。

2. 代码

2.1. 说明

try_mock.func = mock_decorator(try_mock.func.__wrapped__)

2.2. 文件try_mock.py

# filename: try_mock.py
from functools import wraps
import pytest
import mock
import try_mock


def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print(f'In my_decorator {args}')
        return f(*args, **kwargs)
    return wrapper


def _call(x):
    return '_call {}'.format(x)


@my_decorator
def func(x):
    return 'func {}'.format(_call(x))


const_string = 'mock data 1122'
def mock_decorator(f):
    @wraps(f)
    def decorated_func(*args, **kwargs):
        print(f'In mock_decorator {args}')
        return const_string
        # return f(*args, **kwargs)
    return decorated_func


@mock.patch('try_mock._call', return_value='xx')
def test_func_mock(mock_call):
    for x in [11, 22, 33]:
        ret = func(x)
        assert mock_call.called
        assert mock_call.call_args == ((x,),)
        assert ret == 'func xx'


def test_func_mock1():
    try_mock.func = mock_decorator(try_mock.func.__wrapped__)
    for x in [11, 22, 33]:
        ret = try_mock.func(x)
        assert ret == const_string


if __name__ == '__main__':
    print(try_mock.func(11))

2.3. 正常运行

$ python try_mock.py 
In my_decorator (11,)
func _call 11

2.4. 执行测试

$ pytest -vs try_mock.py::test_func_mock1
====================================================================== test session starts =======================================================================
platform linux -- Python 3.8.5, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- /home/shuzhang/soft/miniconda3/bin/python
cachedir: .pytest_cache
rootdir: /home/shuzhang/code/try/python
collected 1 item                                                                                                                                                 

try_mock.py::test_func_mock1 In mock_decorator (11,)
In mock_decorator (22,)
In mock_decorator (33,)
PASSED
$ pytest -vs try_mock.py
====================================================================== test session starts =======================================================================
platform linux -- Python 3.8.5, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- /home/shuzhang/soft/miniconda3/bin/python
cachedir: .pytest_cache
rootdir: /home/shuzhang/code/try/python
collected 2 items                                                                                                                                                

try_mock.py::test_func_mock In my_decorator (11,)
In my_decorator (22,)
In my_decorator (33,)
PASSED
try_mock.py::test_func_mock1 In mock_decorator (11,)
In mock_decorator (22,)
In mock_decorator (33,)
PASSED

3. 总结

对于Python这种动态语言,项目的自动化单元测试是很有必要的,尤其是在多人协作的团队。
然而,单元测试的开发时间和难度,可能比正常功能的开发要久、要麻烦。尽可能实现即可,宗旨是为正常功能提供一个可靠保障。
遇到的问题,除了本文讲到的Decorator的mock,还有Test环境的初始化,例如测试数据库启动和退出、测试数据准备和销毁等。

上一篇下一篇

猜你喜欢

热点阅读