mock
MagicMock 经常接触到的类
参见
应用案例: Lib/test/test/test_asynciotest_base_events.py line:30。 在这个例子中,是模块级的mock,使用MagicMock mock整个socket模块,然后继续使用MagicMock mock 模块里的socket函数。
patch 估计是用得最多的函数
target target传入一个字符串,格式为’package.module.ClassName’, 基本原则是你在查找对象的地方做补丁,这个地方不一定和定义对象的地方相同。
小技巧
如果不确定target的值,可以把要mock的对象打印出来,打印的内容可以帮你快速确定target的值。
警告
双下划线开头的方法不能mock
new 如果传None,则mock对象不会作为参数传入到测试函数
def func():
pass
class T(TestCase):
@mock.patch('__main__.func', new=None)
def test_case1(self):
pass
@mock.patch('__main__.func')
def test_case2(self, mock_b):
pass
spec 创建指定的属性和方法
传入一个列表时:
m = mock.Mock(spec=['attr1', 'attr2'])
print(m.attr1)
print(m.attr2)
传入True时,创建被Mock对象一样的方法, 属性需要自行手工创建。
传入一个class对象或者实例对象, 创建class对象或者实例对象一样的属性和方法
spec_set spec更严格的变体,功能一致。指定spec参数,可以为mock对象创建不存在的属性赋值。指定spec_set参数,为mock对象不存在的属性赋值则抛出异常。
new_callable 替换默认的MagicMock对象。也就是可以自定义个Mock对象。
多次调用同一函数,验证每次传入的参数
from unittest.mock import Mock, call
# 【setup】 -------------------
mock = Mock(return_value=None)
mock(1)
mock(2)
mock(3)
mock(4)
calls = [call(2), call(3)]
# 【assert】 --------------------
mock.assert_has_calls(calls)
calls = [call(4), call(2), call(3)]
# 不限制调用顺序
mock.assert_has_calls(calls, any_order=True)
跟踪方法调用了几次,传入了哪些参数
class SMTP:
def send(self):
print(self)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
...
with patch('__main__.SMTP') as smtp:
with SMTP() as s:
s.send()
# 追踪s的调用情况, s等价于smtp.return_value, 不要使用smtp(),这样是重新实例化一次了
print(smtp.return_value.__enter__.return_value.call_count)
小技巧
对于返回实例的,使用return_value替代()来追踪实例的调用过程, 使用()是调用一次方法了
常见问题
如何mock内置函数
@mock.patch('builtins.open')
如果在自定义的package.module中使用set,想mock set类:
@mock.patch('package.module.set')
参考自 https://docs.python.org/zh-cn/3/library/unittest.mock.html#patch-builtins
Mock和MagicMock的区别
MagicMock预实现了魔术方法(__iter__, __enter__这种), 其他没有区别
patch和patch.object的区别
patch的target参数传入一个字符串,patch.object的target传入一个实例化对象,其他没有区别
如何mock super()的调用
假设B类继承自A类,mock B调用A.func方法, super(B, self).func()
@mock.path(A.func)
开发技巧
简化patch的target写入
灵感来自test_asyncio.test_base_events.py patch_socket函数
使用with来patch
如果一个test_方法里面mock对象有不同的测试场景,可以多次使用with的方式比修饰器更好些。
样例: test_asyncio.test_base_events.py 的test_default_exc_handler_broken方法。
使用Mock作为self调用真实的方法
只能逐个方法设置:
class A:
def __init__(self):
pass
def print(self):
print(self)
# 深拷贝A类
B = type('B', (A.__base__,), dict(A.__dict__))
with mock.patch('__main__.A') as o:
a = A()
print(a.print)
# Mock调用
a.print()
def _print():
_self = a
B.print(_self)
o.print.side_effect = _print
# 真实调用,不过self为Mock类
a.print()
在pytest使用mock
使用 pytest-mock 插件