在单元测试时,有些数据需要依赖其他服务或者不好获取到,此时需要使用mock来模拟对应的函数、对象等。
?
mock模拟数据的python框架:
unittest.mock, 标准模块;,有基于此的mock扩展包;
faker 生成假数据
pytest-mock, 扩展模块
?
mock 作用:
from unittest.mock import Mock
class Lauf:
def __init__(self, name, age):
self.name = name
self.age = age
# 创建对象
lauf = Lauf("jack", 25)
mock_obj = Mock(spec=lauf) # mock对象具有lauf的属性、方法,可以进行属性赋值
mock_obj.name = "lili"
# side_effect 用于引发异常
mock = Mock(side_effect=KeyError('foo'))
mock()
# 抛出异常
KeyError: 'foo'
mock.side_effect = 可重新赋值
# side_effect 用于执行函数动作
m4 = Mock(side_effect=lambda: print("执行函数"))
m4()
执行函数
m4.mock_calls # 查看调用记录
# return_value 直接指定返回值
In [58]: m5 = Mock(return_value="jack")
In [59]: m5()
Out[59]: 'jack'
# wraps 包裹
In [67]: def func():
...: print("func is running...")
...:
In [68]: m8 = Mock(wraps=func)
In [69]: m8()
func is running...
from unittest.mock import MagicMock
class Lauf:
def __init__(self, name, age):
self.name = name
self.age = age
# 模拟方法
lauf.run_method = MagicMock(return_value="running...")
lauf.run_method(3,4,5,key="value") # 参数随意
'running...'
# 断言
lauf.run_method.assert_called_once_with(3,4,5,key="value") # 带着这些参数被调用一次
?
from unittest.mock import patch
In [83]: @patch("os.path")
...: def func(a): # 模拟os.path得到一个MagicMock对象,传给函数
...: print(a, a is os.path)
...:
...:
In [84]: func()
<MagicMock name='path' id='1620313178896'> True
# 依次模拟,得到多个MagicMock对象
@patch("requests.post")
@patch("requests.get")
def func(get_mock, post_mock):
print(get_mock is requests.get) # True
print(post_mock is requests.post) # True
# 模拟类的对象
# 为类的对象的属性、方法(必须该类中存在) 创建一个MagicMock对象
with patch.object(Lauf, 'method', return_value=None) as mock_method:
lauf = Lauf('a', 10)
lauf.method(1, 2, 3)
# 断言
mock_method.assert_called_once_with(1, 2, 3)
# 上下文内有效
foo = {'key': 'value'}
original = foo.copy()
with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
assert foo == {'newkey': 'newvalue'} # 测试时foo变为新字典
# 测试结束,foo恢复
assert foo == original
# 操作魔法方法
m = MagicMock()
m.__str__.return_value = "jack"
str(m) # 返回"jack"
m.__str__ = MagicMock(return_value="xxx")
?
In [98]: def func(a,b,c):
...: print(a,b,c)
...:
# 模拟函数,并确保参数相同
In [99]: mock_func = create_autospec(func, return_value="3")
In [100]: mock_func(1,2,3)
Out[100]: '3'
# 模拟对象,并确保相同的接口
In [106]: mock_obj = create_autospec(Lauf("jack", 23))
In [107]: mock_obj.name
Out[107]: <NonCallableMagicMock name='mock.name' spec='str' id='1620277710272'>
mock_obj.name = "lili" # 赋值
In [108]: mock_obj.age
Out[108]: <NonCallableMagicMock name='mock.age' spec='int' id='1620302996240'>
?
mock = Mock()
# 调用mock
mock()
# 断言
mock.assert_called()
# 返回一个新的Mock对象
mock.method()
<Mock name='mock.method()' id='...'>
# mock.xx 随即返回一个新的 mock对象,新的mock对象断言
mock.method .assert_called()
mock = Mock()
mock.assert_called_once() # 仅仅调用一次,多/没调用 均异常
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_called_with(1, 2, 3, test='wow')
mock = Mock(return_value=None)
mock('foo', bar='baz')
mock.assert_called_once_with('foo', bar='baz')
mock('other', bar='values')
mock.assert_called_once_with('other', bar='values')
Traceback (most recent call last):
...
AssertionError: Expected 'mock' to be called once. Called 2 times.
mock = Mock(return_value=None)
mock(1, 2, arg='thing')
mock('some', 'thing', 'else')
mock.assert_any_call(1, 2, arg='thing')
?
pip install pytest pytest-mock