1. 概述
Python项目的自动化单元测试,可以通过Tox和Pytest完成,提高Python项目的可维护性和健壮性。
关于Tox和Pytest的使用方法,可以参考我之前几篇文章:
本文重点介绍一下通过pytest如何mock一个decorator
,应用场景比如有登录权限限制的API函数
、有无限retry的函数
,等。
2. 代码
2.1. 说明
- Decorator需要是使用
functools.wraps
生成的,否则,需要参考下面的mock原理,应该也ok - 常规的mock方法没有用,例如Mock.return_value、Mock.side_effect
- 正确的mock方法如下,和
unitest.mock
无关,只是重新创造func
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. 执行测试
- 执行单个func的测试
$ 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
- 执行所有test cases
$ 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环境的初始化,例如测试数据库启动和退出、测试数据准备和销毁
等。
网友评论