Python Unit Test - 5 mock-2
Posted Rolei_zl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python Unit Test - 5 mock-2相关的知识,希望对你有一定的参考价值。
>>>>>>>>>>>>>>>>>>>>> 内容实际应用不足,待补充
3. MagicMock -- 实现了部分mock内置magic method,mock中需要手动定义的可直接命名用magicmock调用其方法
Magic Mock,mock子类,在创建Magic Mock对象的同时创建了Mock的所有Magic方法。用来模拟python协议方法,以此用来替代实现python协议的容器或对象。
Mock的magic方法指标记为__X__的mock内置方法。
Mock时magic的方法不会预创建相关方法,需要手工创建;而MagicMock可以预创建所有magic方法,因此可以直接使用。
* If you use the spec keyword argument to create a mock then attempting to set a magic method that isn’t in the spec will raise an AttrributeError.
如果创建mock对象时使用了spec关键字,并使用spec关键字所没有的magic方法,会报属性错误。 -- mock的magic需要手工创建,而不像magicmock一样预创建。
* MagicMock is a subclass of Mock with default implementations of most of the magic methods. You can use MagicMock without having to configure the magic methods yourself.
* If you use the spec or spec_set arguments then only magic methods that exist in the spec will be created.
MagicMock有两种变体:MagicMock 和 NonCallableMagicMock
参考:https://docs.python.org/3/library/unittest.mock.html#magicmock-and-magic-method-support,mock所支持的magic方法
from unittest.mock import Mock
from unittest.mock import MagicMock
class tc():
tl1 = []
def __init__(self):
pass
def tc1():
return "tc1"
## mock
mock = Mock()
## mock spec
mock.mock_add_spec(tc)
mock.tl1
mock.tc1
mock.tl2 # spec:tc, 无tl2定义,报错;AttributeError: Mock object has no attribute 'tl2'
#### mock的__iter__magic方法无法预创建,需要手工创建
# mock.__iter__ # raise AttributeError(name), AttributeError: __iter__
mock.__iter__ = mock.return_value()
print(mock)
## magicmock
magicmock = MagicMock()
#### 预创建magic方法可直接调用
magicmock.__iter__
print(magicmock)
>>> mock.__len__
<function NonCallableMock.__setattr__.<locals>.<lambda> at 0x00FD2C00>
>>> magicmock.__len__
<MagicMock name='mock.__len__' id='91021680'>
>>> magicmock.__len__()
0
>>>
4. PropertyMock - class unittest.mock.
PropertyMock
(*args, **kwargs) -- PropertyMock设置替代类中的属性、方法或描述
mock实例用做类的属性或其他描述,使用时提供__get__()和__set__()方法定义其调返回值。
对象调用mock获取PropertyMock没有参数的实例,调用mock的value值设置。
from unittest.mock import PropertyMock
pmock = PropertyMock()
class pc1:
def __init__(self, v1):
self.v1 = v1
def p1(self):
print("PropertyMock")
p = pc1(5) # instance class, variable: 5
print(p.v1) # call class variable, return: 5
p.p1() # call class method,return: PropertyMock
pmock.return_value = 'return value'
p.v1 = pmock
print(p.v1) # <PropertyMock id='72342288'>
type(p).v1 = pmock # PorpertyMock as class property
# type(p) call __set__(self, obj, val) and __get__(self, obj, obj_type) method
print(p.v1) # call class variable, return PropertyMock instatnce return_value
pmock.return_value = p.p1
type(p).p1 = pmock
print(p.p1) # <PropertyMock id='79380080'>
p.p1() # PropertyMock
type(p).p1 = pmock # PorpertyMock as class property
# type(p) call __set__(self, obj, val) and __get__(self, obj, obj_type) method
print(p.p1) # call class method, retrun PropertyMock instatnce return_value
p.p1() # class initial value replace by PropertyMock
# TypeError: 'str' object is not callable
5. AsyncMock -- New in version 3.8.
Mock升级版,允许模拟异步函数
MagicMock的异步版本,当对象被识别的异步函数时AsyncMock对象被执行,函数调用结果是可挂起等待的。 -- await 异步等待,程序运行时为了执行某些特殊操作而对在运行线程进行的操作。
当mock()为异步函数,异步等待后会返回side_effect 或 return_value:定义side_effect时返回side_effect,否则返回return_value。
如果输出的是异步函数,模拟对象调用时模拟的异步函数是异步函数自身。
from unittest.mock import AsyncMock, Mock, asyncio
class ac:
def sync():
print('sync')
async def a_sync(i):
print(i)
amock = AsyncMock(ac)
# 类的模拟方法(mock, magicmock, asyncmock)自动检测其调用方法同步或异步
print(amock.sync) # <MagicMock name='mock.sync' id='2810946685872'>
print(amock.a_sync) # <AsyncMock name='mock.a_sync' id='2810946742160'>
async def await_sync(i):
await i
print(i)
i = amock()
print(amock.assert_awaited()) # None
##amock.called # AssertionError: Expected mock to have been awaited.
asyncio.run(await_sync(i)) # <coroutine object AsyncMockMixin._execute_mock_call at 0x0000020DF0146440>
##print(amock.assert_not_awaited()) # AssertionError: Expected mock to not have been awaited. Awaited 1 times.
print(amock.assert_awaited()) # None
amock = AsyncMock(await_sync)
amock() # RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited
Method | 说明 |
assert_awaited() | 断言mock至少异步等待一次 |
assert_awaited_once() | 断言mock只异步等待一次 |
assert_awaited_with(*args, **kwargs) | 断言上次以特定参数异步等待 |
assert_awaited_once_with(*args, **kwargs) | 断言mock以特定参数异步等待一次 |
assert_any_await(*args, **kwargs) | 断言任何的异步等待 |
assert_has_awaits(calls, any_order=False) | 断言mock以特定调用异步等待 |
assert_not_awaited() | 断言mock从没有异步等待 |
reset_mock(*args, **kwargs) | 重置mock设置 |
await_count | 异步等待次数 |
await_args | 异步等待参数 |
await_args_list | 异步等待参数列表 |
6. patchers ?
装饰器:函数对象作为参数传递。
patch装饰器,用来在一定的功能范围内包装对象。方便使用mock对象临时替代特定模块中的类。默认patch会创建MagicMock指定Mock的替代类。
patch(),函数、类或内容管理,用来patch新对象。
unittest.mock.
patch
(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
>> target:引用自类方法;从调用的patch()中引用,当@decorator函数执行时被引用,'package.module.ClassName'
>> patch().start / patch().stop:通过patch()方法mock对象
from unittest.mock import Mock, patch
import pmock # self define class
patcher = patch("pmock.func",spec='patcher',first='1') # define target: pmock.func
mock= patcher.start() # mock path() object
print(mock.first) # 1: call mock methoc
patcher.stop()
def tp():
print("patcher test")
@patch("pmock.func",spec='patcher',first="patcher check first")
def test(f):
tp() # patcher test
print(f.first) # patcher check first
print(pmock.func) # <NonCallableMagicMock name='func' spec='str' id='3121040519120'>
assert pmock.func == "patcher check" # AssertionError
test()
patch | 说明 |
object | patch对象属性 |
dict | patch字典类型 |
multiple | 在一个调用中命名用多个patcher |
start | 使用patch,put the patch in place |
stop | undo it |
7. Helper
Helpers | 说明 |
sentinel | (哨兵)为测试提供特定唯一对象的简单方法,提供特定参数或返回值的方法,使得返回信息可读 |
DEFAULT | 预创建的sentinel,定义正常的返回值 |
call | 对mock方法调用实现简单断言:call_args, call_args_list, mock_calls |
create_autospec | 用另一对象作为spec创建新的mock对象,mock对象默认使用spec对象的属性 ? |
ANY | 实现某个特定目的的复杂断言,并不关心参数 |
FILTER_DIR | 模块级变量,用来控制和过滤mock对象的dir()方法显示内容 ? |
mock_open | mock open()方法,替代open()方法 ? |
Autospeccing | 基于spec特性,限制初始对象的mock api ? |
Sealing mocks | seal()。当被访问的mock对象的属性被seal或属性递归时,sell用来禁止自动创建mock对象 ? |
from unittest.mock import Mock, sentinel, call, MagicMock, ANY
class chelp:
def __init__(self):
pass
def pval(self):
print("this is helper")
ch = Mock(spec='helper', name='chelper', return_value='this test',DEFAULT='default')
# sentinel / DEFAULT
print(ch.DEFAULT) # default
ch.DEFAULT=sentinel.DEFAULT
print(ch.DEFAULT) # sentinel.DEFAULT
# call(*args, **kwargs)
ch(1,a='a')
print(ch.call_args_list == [call(1,a='a'),call()]) # False
ch() # call mock
print(ch.call_args_list == [call(1,a='a'),call()]) # True
# call_list()
mch = MagicMock()
mch(1).method(2).other(3)(4,5)
tcall = call(1).method(2).other(3)(4,5)
print(tcall.call_list())
'''
[call(1),
call().method(2),
call().method().other(3),
call().method().other()(4, 5)]
'''
print(mch.mock_calls == tcall.call_list()) # True
# ANY
print(mch.mock_calls == [call(1),call(2),ANY]) # False
mch.__init__()
mch(1)
mch(2)
mch(3)
print(mch.mock_calls == [call(1),call(2),ANY]) # True
以上是关于Python Unit Test - 5 mock-2的主要内容,如果未能解决你的问题,请参考以下文章