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中内置的日志模块logging用法详解
Jul 12 Python
python入门前的第一课 python怎样入门
Mar 06 Python
python实现百度语音识别api
Apr 10 Python
Django实现全文检索的方法(支持中文)
May 14 Python
python 实现批量xls文件转csv文件的方法
Oct 23 Python
python 提取tuple类型值中json格式的key值方法
Dec 31 Python
网易有道2017内推编程题 洗牌(python)
Jun 19 Python
Python如何使用BeautifulSoup爬取网页信息
Nov 26 Python
Python面向对象程序设计之私有变量,私有方法原理与用法分析
Mar 23 Python
Python urlopen()参数代码示例解析
Dec 10 Python
Python中的min及返回最小值索引的操作
May 10 Python
Python OpenCV超详细讲解读取图像视频和网络摄像头
Apr 02 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
Thinkphp使用mongodb数据库实现多条件查询方法
2014/06/26 PHP
ThinkPHP使用Smarty第三方插件方法小结
2016/03/19 PHP
php判断用户是否关注微信公众号
2016/07/22 PHP
Nginx下ThinkPHP5的配置方法详解
2017/08/01 PHP
PHP调用API接口实现天气查询功能的示例
2017/09/21 PHP
PHP实现求两个字符串最长公共子串的方法示例
2017/11/17 PHP
用Javscript实现表单复选框的全选功能
2007/05/25 Javascript
ie与ff下的event事件使用介绍
2013/11/25 Javascript
制作jquery遮罩层效果导航菜单代码分享
2013/12/25 Javascript
JavaScript window.location对象
2014/11/14 Javascript
javascript中Array()数组函数详解
2015/08/23 Javascript
Javascript控制div属性动态变化实例分析
2015/10/08 Javascript
javascript设计模式之module(模块)模式
2016/08/19 Javascript
详解nodejs操作mongodb数据库封装DB类
2017/04/10 NodeJs
JavaScript实现AOP详解(面向切面编程,装饰者模式)
2017/12/19 Javascript
JS实现数组的增删改查操作示例
2018/08/29 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
vue实现的封装全局filter并统一管理操作示例
2020/02/02 Javascript
vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)
2021/03/01 Vue.js
[46:27]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#2LGD VS MVP.Phx第一局
2016/03/02 DOTA
详解Django中的ifequal和ifnotequal标签使用
2015/07/16 Python
Python设置在shell脚本中自动补全功能的方法
2018/06/25 Python
对python创建及引用动态变量名的示例讲解
2018/11/10 Python
详解pytorch 0.4.0迁移指南
2019/06/16 Python
numpy.meshgrid()理解(小结)
2019/08/01 Python
CSS3五个技巧给你的网站带来出色的效果
2009/04/02 HTML / CSS
Urban Outfitters德国官网:美国跨国生活方式零售公司
2018/05/21 全球购物
乐高西班牙官方商店:LEGO Shop ES
2019/12/01 全球购物
mysql_pconnect()和mysql_connect()有什么区别
2012/05/25 面试题
Prototype如何为一个Ajax添加一个参数
2015/12/06 面试题
车间班长岗位职责
2013/11/30 职场文书
幼儿园保育员辞职信
2014/01/12 职场文书
教师党员批评与自我批评发言稿
2014/10/15 职场文书
三年级作文之趣事作文
2019/11/04 职场文书
vue实现可拖拽的dialog弹框
2021/05/13 Vue.js
MySQL数据库压缩版本安装与配置详细教程
2021/05/21 MySQL