python之mock模块基本使用方法详解


Posted in Python onJune 27, 2019

mock简介

mock原是python的第三方库

python3以后mock模块已经整合到了unittest测试框架中,不用再单独安装

Mock这个词在英语中有模拟的意思,因此我们可以猜测出这个库的主要功能是模拟一些东西

准确的说,Mock是Python中一个用于支持单元测试的库,它的主要功能是使用mock对象替代掉指定的Python对象,以达到模拟对象的行为

既然mock已经被整合到了unittest单元测试框架中,可想而知mock的目的就是为了让我们更好的进行测试

mock作用

1. 解决依赖问题:当我们测试一个接口或者功能模块的时候,如果这个接口或者功能模块依赖其他接口或其他模块,那么如果所依赖的接口或功能模块未开发完毕,那么我们就可以

使用mock模拟被依赖接口,完成目标接口的测试

2. 单元测试:如果某个功能未开发完成,我们又要进行测试用例的代码编写,我们也可以先模拟这个功能进行测试

3. 模拟复杂业务的接口:实际工作中如果我们在测试一个接口功能时,如果这个接口依赖一个非常复杂的接口业务,那么我们完全可以使用mock来模拟这个复杂的业务接口,其实

这个和解决接口依赖是一样的原理

4.前后端联调:如果你是一个前端页面开发,现在需要开发一个功能:根据后台返回的状态展示不同的页面,那么你就需要调用后台的接口,但是后台接口还未开发完成,是不是你

就停止这部分工作呢?答案是否定的,你完全可以借助mock来模拟后台这个接口返回你想要的数据

mock安装

python 3 的mock模块已经被整合到了unittest框架中,所以你使用的时候只需要在文件开头from unittest import mock 导入即可

如果你使用的是python2 那么你需要执行pip install mock安装后再 import mock即可

mock实例

一个未开发完成的功能如何测试?

假如们现在有一个实现两个数相加的功能需要编写测试用例,但是由于开发进度缓慢,只搭两个简单的框架,并没有内部实现

"""
------------------------------------
@Time : 2019/6/26 14:09
@Auth : linux超
@File : ClassFunc.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import unittest
from unittest import mock
class SubClass(object):
def add(self, a, b):
"""两个数相加"""
pass
class TestSub(unittest.TestCase):
"""测试两个数相加用例"""
def test_sub(self):
sub = SubClass() # 初始化被测函数类实例
sub.add = mock.Mock(return_value=10) # mock add方法 返回10
result = sub.add(5, 5) # 调用被测函数
self.assertEqual(result, 10) # 断言实际结果和预期结果
if __name__ == '__main__':
unittest.main()

测试结果

.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Process finished with exit code

测试结果显示,测试用例执行已经通过

实际上mock模拟add方法的原理是 使用相同的对象方法接收mock的对象(使用sub.add接收),那么当mock对象被调用时(sub.add())就会返回return_value参数对应的数据

这样一来,表面看起来就是模拟了add方法(这里只是我个人理解,不对请忽略)

你可以做一个实验,把用例中的add改成别的名字也一样可以测试通过

ok,继续

我们用例编写完了,而且开发既然也把功能开发完了(要骂街吗?),既然真实的功能已经可以测试了,那么我们怎么在上面用例的基础上直接测试真实功能呢?

完整的功能如何测试?

我们把用例的代码稍做修改

"""
------------------------------------
@Time : 2019/6/26 14:09
@Auth : linux超
@File : ClassFunc.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import unittest
from unittest import mock
class SubClass(object):
def add(self, a, b):
"""两个数相加"""
return a + b
class TestSub(unittest.TestCase):
"""测试两个数相加"""
def test_sub(self):
sub = SubClass() # 初始化被测函数类实例
sub.add = mock.Mock(return_value=10, side_effect=sub.add) # 传递side_effect关键字参数, 会覆盖return_value参数值, 使用真实的add方法测试
result = sub.add(5, 11) # 真正的调用被测函数
self.assertEqual(result, 16) # 断言实际结果和预期结果
if __name__ == '__main__':
unittest.main()

side_effect参数

代码中我们给Mock方法添加了另一个关键字参数side_effect = sub.add, 这个参数和return_value 正好相反,当传递这个参数的时候return_value 参数就会失效

而side_effect生效,这里我给的参数值是sub.add 相当于add方法的地址,那么当调用add方法时就会真实的使用add方法,也就达到了我们测试实际的add 方法。

你也可以理解为当传递了side_effect参数且值为被测方法地址时,mock就不会起作用

side_effect接收的是一个可迭代序列,当传递多个值时,那么每次调用mock时会返回不同的值

mock_obj = mock.Mock(side_effect= [1,2,3])
print(mock_obj())
print(mock_obj())
print(mock_obj())
print(mock_obj())
输出
Traceback (most recent call last):
1
File "D:/MyThreading/mymock.py", line 37, in <module>
2
print(mock_obj())
3
File "C:\Python36\lib\unittest\mock.py", line 939, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "C:\Python36\lib\unittest\mock.py", line 998, in _mock_call
result = next(effect)
StopIteration
Process finished with exit code 1

当所有值被取完后就会报错(这个地方有点类似生成器的原理)

存在依赖关系的功能如何测试?

假设有这样一个场景:我们要测试一个支付接口但是这个支付接口又依赖一个第三方支付接口,那么第三方支付接口我们暂时没有权限使用,那么我们该如何测试我们自己这个接口呢?

看下面的实例

假设第三方接口和我们自己的支付接口如下

"""
------------------------------------
@Time : 2019/6/26 15:09
@Auth : linux超
@File : PayMent.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import requests
class PayApi(object):
@staticmethod
def auth(card, amount):
"""
第三方支付接口
:param card: 卡号
:param amount: 支付金额
:return:
"""
pay_url = "http://www.zhifubao.com" # 第三方支付接口地址
data = {"card": card, "amount": amount}
response = requests.post(pay_url, data=data) # 请求第三方支付接口
return response # 返回状态码
def pay(self, user_id, card, amount):
"""
我们自己的支付接口
:param user_id: 用户id
:param card: 卡号
:param amount: 支付金额
:return:
"""
 # 调用第三方支付接口
response = self.auth(card, amount)
try:
if response['status_code'] == '200':
print('用户{}支付金额{}成功'.format(user_id, amount))
return '支付成功'
elif response['status_code'] == '500':
print('用户{}支付失败, 金额不变'.format(user_id))
return '支付失败'
else:
return '未知错误'
except Exception:
return "Error, 服务器异常!"
if __name__ == '__main__':
pass

很明显第三方支付接口是无法访问的,因为接口的地址是我DIY的,为了模拟实际中我们无法使用的第三方支付接口

编写测试用例

"""
------------------------------------
@Time : 2019/6/26 15:22
@Auth : linux超
@File : testpay.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import unittest
from unittest import mock
from payment.PayMent import PayApi
class TestPayApi(unittest.TestCase):
def test_success(self):
pay = PayApi()
pay.auth = mock.Mock(return_value={'status_code':'200'})
status = pay.pay('1000', '12345', '10000')
self.assertEqual(status, '支付成功')
def test_fail(self):
pay = PayApi()
pay.auth = mock.Mock(return_value={'status_code':'500'})
status = pay.pay('1000', '12345', '10000')
self.assertEqual(status, '支付失败')
def test_error(self):
pay = PayApi()
pay.auth = mock.Mock(return_value={'status_code':'300'})
status = pay.pay('1000', '12345', '10000')
self.assertEqual(status, '未知错误')
def test_exception(self):
pay = PayApi()
pay.auth = mock.Mock(return_value='200')
status = pay.pay('1000', '12345', '10000')
self.assertEqual(status, 'Error, 服务器异常!')
if __name__ == '__main__':
unittest.main()

测试输出结果

....用户1000支付失败, 金额不变
用户1000支付金额10000成功
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Process finished with exit code 0

从执行结果可以看出,即使第三方支付接口无法使用,但是我们自己的支付接口仍然测试通过了

也许有人会问,第三方支付都不能用,我们的测试结果是否是有效的呢?

通常我们在测试一个模块的时候,我们是可以认为其他模块的功能是正常的,只针对目标模块进行测试是没有任何问题的,所以说测试结果也是正确的

其实上述代码还可以使用另一种方式来写

mock对象的方法

"""
------------------------------------
@Time : 2019/6/26 15:22
@Auth : linux超
@File : testpay.py
@IDE : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import unittest
from unittest import mock
from unittest.mock import patch
from payment.PayMent import PayApi
class TestPayApi(unittest.TestCase):
def setUp(self):self.pay = PayApi()
@patch.object(PayApi, 'auth')
def test_success(self, mock_auth):
mock_auth.return_value = {'status_code':'200'}
status = self.pay.pay('1000', '12345', '10000')
self.assertEqual(status, '支付成功')
@patch.object(PayApi, 'auth')
def test_fail(self, mock_auth):
mock_auth.return_value={'status_code':'500'}
status = self.pay.pay('1000', '12345', '10000')
self.assertEqual(status, '支付失败')
@patch.object(PayApi, 'auth')
def test_error(self, mock_auth):
mock_auth.return_value={'status_code':'300'}
status = self.pay.pay('1000', '12345', '10000')
self.assertEqual(status, '未知错误')
@patch.object(PayApi, 'auth')
def test_exception(self, mock_auth):
mock_auth.return_value='200'
status = self.pay.pay('1000', '12345', '10000')
self.assertEqual(status, 'Error, 服务器异常!')
if __name__ == '__main__':
unittest.main()

还有mock一个普通函数,mock多个方法等,这里先不赘述,写法和上面实例差不多

最后

mock还有很多自带的功能方法

且mock功能很强大,也不是一句两句话就能说完了,本篇文章主要介绍了mock的基本使用方法,甚是简单,对于实际中如何应用,如何掌握更强大的方法还需自己慢慢摸索

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python 获取文件列表(或是目录例表)
Mar 25 Python
C#返回当前系统所有可用驱动器符号的方法
Apr 18 Python
python开发之thread线程基础实例入门
Nov 11 Python
Python中的descriptor描述器简明使用指南
Jun 02 Python
django使用图片延时加载引起后台404错误
Apr 18 Python
Python实现PS滤镜功能之波浪特效示例
Jan 26 Python
python中的协程深入理解
Jun 10 Python
python3中利用filter函数输出小于某个数的所有回文数实例
Nov 24 Python
Pytorch转onnx、torchscript方式
May 25 Python
如何学习Python time模块
Jun 03 Python
Python用requests库爬取返回为空的解决办法
Feb 21 Python
Python 游戏大作炫酷机甲闯关游戏爆肝数千行代码实现案例进阶
Oct 16 Python
python文件选择对话框的操作方法
Jun 27 #Python
python开启debug模式的方法
Jun 27 #Python
python判断所输入的任意一个正整数是否为素数的两种方法
Jun 27 #Python
教你一步步利用python实现贪吃蛇游戏
Jun 27 #Python
Python中遍历列表的方法总结
Jun 27 #Python
Python 把序列转换为元组的函数tuple方法
Jun 27 #Python
Python使用numpy模块实现矩阵和列表的连接操作方法
Jun 26 #Python
You might like
海贼王:最美的悬赏令!
2020/03/02 日漫
php download.php实现代码 跳转到下载文件(response.redirect)
2009/08/26 PHP
PHP正则提取不包含指定网址的图片地址的例子
2014/04/21 PHP
php简单实现查询数据库返回json数据
2015/04/16 PHP
PHP实现的简单适配器模式示例
2017/06/22 PHP
php读取本地json文件的实例
2018/03/07 PHP
情人节专属 纯js脚本1k大小的3D玫瑰效果
2012/02/11 Javascript
javascript动态向网页中添加表格实现代码
2014/02/19 Javascript
jQuery中noconflict函数的实现原理分解
2015/02/03 Javascript
jquery中filter方法用法实例分析
2015/02/06 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
Angular页面间切换及传值的4种方法
2016/11/04 Javascript
详解使用webpack构建多页面应用
2017/12/21 Javascript
vue2.0 实现导航守卫(路由守卫)
2018/05/21 Javascript
原生JS forEach()和map()遍历的区别、兼容写法及jQuery $.each、$.map遍历操作
2019/02/27 jQuery
详解vue-cli 脚手架 安装
2019/04/16 Javascript
JavaScript原型式继承实现方法
2019/11/06 Javascript
[01:41]DOTA2 2015国际邀请赛中国区预选赛第三日战报
2015/05/28 DOTA
Python Socket编程入门教程
2014/07/11 Python
pymongo为mongodb数据库添加索引的方法
2015/05/11 Python
Python实现二叉堆
2016/02/03 Python
Python中多个数组行合并及列合并的方法总结
2018/04/12 Python
python3.5安装python3-tk详解
2019/04/26 Python
python实现画图工具
2020/08/27 Python
python实现AHP算法的方法实例(层次分析法)
2020/09/09 Python
python 实现socket服务端并发的四种方式
2020/12/14 Python
HTML5 Canvas 实现K线图的示例代码
2019/12/23 HTML / CSS
Staples美国官方网站:办公用品一站式采购
2016/07/28 全球购物
Java Servlet API中forward() 与redirect()的区别
2014/04/20 面试题
人事档案接收函
2014/01/12 职场文书
2014年公司迎新年活动方案
2014/02/24 职场文书
小学英语课后反思
2014/04/26 职场文书
授权委托书范本(单位)
2014/09/28 职场文书
2014年校长工作总结
2014/12/11 职场文书
Python进程间的通信之语法学习
2022/04/11 Python
微软发布Windows 11今年最大更新22H2(附 ISO 镜像官方下载)
2022/09/23 数码科技