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(callsany_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(targetnew=DEFAULTspec=Nonecreate=Falsespec_set=Noneautospec=Nonenew_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说明
objectpatch对象属性
dictpatch字典类型
multiple在一个调用中命名用多个patcher
start

使用patch,put the patch in place

stopundo 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_openmock open()方法,替代open()方法 ?
Autospeccing基于spec特性,限制初始对象的mock api ?
Sealing mocksseal()。当被访问的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的主要内容,如果未能解决你的问题,请参考以下文章

如何在Java Unit Test中mock 静态方法

TDD学习笔记一Unit Test - Stub, Mock, Fake 简介

Java Unit Test

Spring Boot的单元测试(Unit Test)

Spring Boot的单元测试(Unit Test)

在创建新的 Rails 应用程序时,如何告诉 Rails 使用 RSpec 而不是 test-unit?