========================================= mock ========================================= **MagicMock** 经常接触到的类 .. seealso:: 应用案例: Lib/test/test/test_asyncio\test_base_events.py line:30。 在这个例子中,是模块级的mock,使用MagicMock mock整个socket模块,然后继续使用MagicMock mock 模块里的socket函数。 **patch** 估计是用得最多的函数 * target target传入一个字符串,格式为'package.module.ClassName', 基本原则是你在查找对象的地方做补丁,这个地方不一定和定义对象的地方相同。 .. tip:: 如果不确定target的值,可以把要mock的对象打印出来,打印的内容可以帮你快速确定target的值。 .. warning:: 双下划线开头的方法不能mock * new 如果传None,则mock对象不会作为参数传入到测试函数 .. code-block:: python 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对象。 多次调用同一函数,验证每次传入的参数 ----------------------------------------- .. code-block:: python 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) 跟踪方法调用了几次,传入了哪些参数 ------------------------------------------------ .. code-block:: python 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) .. tip:: 对于返回实例的,使用return_value替代()来追踪实例的调用过程, 使用()是调用一次方法了 ------------------------------------------------ 常见问题 ----------------------------------------------- 如何mock内置函数 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: @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() .. code-block:: @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 `_ 插件