mock介绍
当一个功能依赖另一个功能时,而这个功能还没有完善,需要使用mock来模拟依赖的返回
mock主要有以下几个库:
- unittest.mock:python内置的用于mock的库,有Mock,MagicMock,path等类可以使用
- pytest-mock: 第三方插件库
- monkeypatch:pytest内置的fixture,也可以提供mock功能
- mockito:第三方库
where to mock
要使用mock,首先得明白mock的原理,在哪个地方mock
以unittest.mock来举例:
使用Mock()或者MagicMock()会生成一个Mock或者MagicMock对象,当调用时,就会返回mock的值。如下:
from unittest.mock import Mock,MagicMock
f=Mock(return_value=34)
print(f())
print(f)
Mock(return_value=34),初始化生成一个Mock对象,这个对象在内存中有一个地址,地址是以140开头的
f=Mock(return_value=34),将这个地址赋值给了f,此时,f的地址就是Mock id=’140295868166688′
当调用f()时就会返回Mock对象的值
所以猜测,Mock是通过将Mock对象的地址赋值给要调用的函数或者方法的地址,改变返回值
所以问题是:在哪里mock?
在方法调用的模块或者查找的模块mock
The key is to patch out SomeClass where it is used (or where it is looked up).
比如:
def add(x,y):
result=x+y
Log.info("result:{}".format(result))
return result
from LearnPytest.task.main1 import add
def my_add_four(x,y):
result=add(x,y)
return result
def test_my_mock():
main4.add=Mock(return_value=567)
result=my_add_four(4,5)
print(result)
def test_my_mock_one():
main1.add=Mock(return_value=900)
result = my_add_four(4, 5)
print(result)
if __name__ == '__main__':
pytest.main(["-v","-s","test_mymock_one.py::test_my_mock"])
test_my_mock这种方式的mock起作用,先分析这种mock方式:
首先生成了一个mock对象,return_value在mock调用时才会返回
运行完main4.add=Mock(return_value=567),这里的main4.add已经被替换为mock对象了,需要运行到main4.py时看add的地址
代码运行到main4.py中查看,add已经被替换为mock对象了,当代码里调用add时,mock对象就会被调用,所以mock才会起作用
test_my_mock_one
执行完成main1.add=Mock(return_value=900),发现main1的add被替换成了mock对象,
继续运行到main4.py,查看add的地址,这里的add的地址不是mock对象的地址,所以mock不起作用
使用另一种import XXX来导入模块时,需要在查到方法的地方mock
import LearnPytest.task.main1
def my_add_five(x,y):
result=LearnPytest.task.main1.add(x,y)
return result
import pytest
from LearnPytest.task.main5 import my_add_five
from LearnPytest.task import main5
from unittest.mock import Mock
from LearnPytest.task import main1
def test_mymock_one():
main5.add=Mock(return_value=800)
result=my_add_five(4,7)
print(result)
def test_mymock_two():
main1.add=Mock(return_value=809)
result=my_add_five(4,5)
print(result)
if __name__ == '__main__':
pytest.main(["-v","-s","test_mymock_two.py::test_mymock_two"])
unittest.mock使用介绍
(1)Mock类的定义
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
其中,return_value是当mock被调用时返回一个值
side_effect:This can either be a function to be called when the mock is called, an iterable or an exception (class or instance) to be raised.
side_effect的值可以是一个function,或者一个可迭代或者一个异常
当mock对象被调用时,传入的function会被以相同的参数调用
Mock还可以替换对象
MagicMock是Mock的子类
Mock objects are callable. The call will return the value set as the return_value attribute. The default return value is a new Mock object; it is created the first time the return value is accessed (either explicitly or by calling the Mock) – but it is stored and the same one returned each time.
Mock对象是可调用的,但是存储起来,所以mock一个方法后,后续再调用这个方法,mock仍在存在,如果想解掉这个mock,需要使用patch装饰器
(2)patch
patch是一个装饰器,只在函数范围内来mock对象,函数结束后,自动解mock,即使测试中遇到异常
The patch decorators are used for patching objects only within the scope of the function they decorate. They automatically handle the unpatching for you, even if exceptions are raised. All of these functions can also be used in with statements or as class decorators.
patch的定义:
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
target:是需要mock的类(被mock的类),是一个string,类似”package.module.ClassName”
当参数new忽略时,要创建的mock对象,作为被patch装饰的的function的一个额外的参数传入
patch会自动创建一个MagicMock的对象
如下是一个如何mock方法的例子:
def my_add(x,y):
result=add(x,y)
return result
@patch("src.my_add.add",)
def test_mock_one(mock_function):
mock_function.return_value=109
result=my_add(4,5)
print(result)
由上图可以看出,patch创建了一个名字为mock_function的MagicMock的对象
这个MagicMock对象如何和src.my_add.add进行绑定,使用target
monkeypatch的使用介绍
monkeypatch是pytest内置的一个fixture
monkeypath能够安全的设置或者删除属性,字典的item以及环境变量
所有的设置在function或者fixture完成后会解绑
monkeypatch提供的方法有:
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.setattr(“somemodule.obj.name”, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
monkeypatch的使用场景有:
(1)改变一个函数或者一个类的属性,通过使用monkeypatch.setattr或者monkeypatch.delattr方法
(2)改变一个字典的值,通过monkeypatch.setitem和monkeypatch.delitem
(3)改变环境变量的值,通过monkeypatch.setenv和monkeypatch.delenv
如下代码:
def mockreturn(x,y):
return 808
def test_mock_two(monkeypatch):
monkeypatch.setattr('src.my_add.add',mockreturn)
result=my_add(6,7)
print(result)
当执行到monkeypatch.setattr时,monkeypatch的属性setattr被赋值了,function的地方是add的地址
当执行到my_add中时,查看add函数,发现它的地址变为mockreturn的地址,返回了我们需要的值
monkeypatch的官网:
https://docs.pytest.org/en/latest/how-to/monkeypatch.html
mockito
mockito是一个第三方库
官方文档:https://mockito-python.readthedocs.io/en/latest/#
from mockito import when, mock, unstub
when(os.path).exists('/foo').thenReturn(True)
import requests
response = mock({'status_code': 200, 'text': 'Ok'})
when(requests).get(...).thenReturn(response)
requests.get('http://google.com/')
unstub()
mockito主要用作同一个方法或者函数,入参不同时,返回的值不同
使用mockito,之后要记得unstub,unstub可以放在teardown中
mock的具体使用场景
如何mock一个函数
from unittest.mock import patch,Mock
import src
from src.my_add import my_add
import pytest
@patch("src.my_add.add",)
def test_mock_one(mock_function):
mock_function.return_value=109
result=my_add(4,5)
print(result)
def mockreturn(x,y):
return 808
def test_mock_two(monkeypatch):
monkeypatch.setattr('src.my_add.add',mockreturn)
result=my_add(6,7)
print(result)
def test_mock_three():
src.my_add.add=Mock(return_value=404)
result=my_add(5,6)
print(result)
如何mock一个类中的方法
class MyAdd():
def __init__(self,x,y):
self.x=x
self.y=y
def first_add(self):
return self.x+self.y
def second_fuction(self):
return self.x-self.y
def my_first_function(x,y):
my_add=MyAdd(x,y)
result=my_add.first_add(x,y)
return result
from src.my_add import my_first_function
import pytest
from unittest.mock import Mock,MagicMock,patch
@patch("src.my_add.MyAdd")
def test_mock_one(mock_class):
a=mock_class.return_value
a.first_add.return_value=608
result=my_first_function(4,5)
print(result)
class MockClass(MagicMock):
first_add=MagicMock(return_value=809)
def test_mock_two():
with patch("src.my_add.MyAdd",MockClass) as mock:
result=my_first_function(6,7)
print(result)
def mockreturn(*args):
return 708
def test_mock_three(monkeypatch):
monkeypatch.setattr("src.my_add.MyAdd.first_add",mockreturn)
result = my_first_function(6, 7)
print(result)
if __name__ == '__main__':
pytest.main(["-v","-s","test_four.py::test_mock_one"])
如何mock,不同参数,返回的值不同
Original: https://blog.csdn.net/ljsykf/article/details/123041724
Author: 爱吃水饺的小京
Title: pytest-mock的使用
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/772240/
转载文章受原作者版权保护。转载请注明原作者出处!