1. Mock测试
##1.1 什么是Mock测试
mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
换句话说,当一个接口依赖于另一个上游接口,恰好上有接口还不能提供服务,那么我们就需要模拟上游返回进行测试。
1.2 为什么要进行mock测试
Mock是为了解决不同的单元之间由于耦合而难以开发、测试的问题,所以,Mock可以出现在单元测试、集成测试、系统测试中。mock最大的功能就是把耦合分解。
例如:一个闹钟根据时间来进行提醒服务,如果过了下午5点钟就播放音频文件提醒大家下班了,如果我们要利用真实的对象来测试的话就只能苦苦等到下午五点,然后把耳朵放在音箱旁,我们应该利用mock对象来进行测试,这样我们就可以模拟控制时间了,而不用苦苦等待时钟转到下午5点钟了
1.3 Mock对象使用场景
1.需要将当前被测单元和其依赖模块独立分开,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑。
** 实例 **
被测试的代码中需要依赖第三方接口返回的值进行逻辑处理,可能因为网络或者其他环境因素,调用第三方接口经常会中断或者失败,无法对被测单元进行测试,这个时候就可以使用mock技术将被测单元和依赖模块独立开,以达到可测性。
2.被测单元以来的模块尚未开发完成,而被测单元需要依赖模块的返回值进行后续处理。
1)前后端项目中,后端接口开发完成之前,接口联调;
2)依赖的上游接口尚未开发完成,需要接口联调测试;
** 实例 **
service代码中,包含对Dao层的调用,但是Dao层还没有实现
3.被测单元以来的对象较难模拟或者不容易构造
** 实例 **
支付宝支付的异常情况有很多,但是模拟这种异常条件很复杂或者无法模拟。
1.4 Mock测试的优势
- 团队可以并行工作
有了Mock,前后端人员只需要定义好接口文档就可以开始并行工作,互不影响,只在最后的联调阶段往来密切;后端与后端之间如果有接口耦合,也同样能被Mock解决;测试过程中如果遇到依赖接口没有准备好,同样可以借助Mock;不会出现一个团队等待另一个团队的情况。这样的话,开发自测阶段就可以及早开展,从而发现缺陷的时机也提前了,有利于整个产品质量以及进度的保证。
2.开启TDD模式,即测试驱动开发
单元测试是TDD实现的基石,而TDD经常会碰到协同模块尚未开发完成的情况,但是有了mock,这些一切都不是问题。当接口定义好后,测试人员就可以创建一个Mock,把接口添加到自动化测试环境,提前创建测试。
3.可以模拟那些无法访问的资源
比如说,你需要调用一个“墙”外的资源来方便自己调试,就可以自己Mock一个。
4.隔离系统
假如我们需要调用一个post请求,为了获得某个响应,来看当前系统是否能正确处理返回的“响应”,但是这个post请求会造成数据库中数据的污染,那么就可以充分利用Mock,构造一个虚拟的post请求,我们给他指定返回就好了。
5.可以用来演示
假如我们需要创建一个演示程序,并且做了简单的UI,那么在完全没有开发后端服务的情况下,也可以进行演示。说到演示了,假如你已经做好了一个系统,并且需要给客户进行演示,但是里面有些真实数据并不想让用户看到,那么同样,你可以用Mock接口把这些敏感信息接口全部替换。
6.测试覆盖度
假如有一个接口,有100个不同类型的返回,我们需要测试它在不同返回下,系统是否能够正常响应,但是有些返回在正常情况下基本不会发生,比如,我们需要测试在当接口发生500错误的时候,app是否崩溃,别告诉我你一定要给服务端代码做些手脚让他返回500 。而使用mock,这一切就都好办了,想要什么返回就模拟什么返回,不用再担心我的测试覆盖度了!
2. Mock测试方式
1.python
实例1:简单使用
import requests
from unittest import mock
def request_my_blog():
res = requests.get("http://www.my/blog")
return res.status_code.encode("utf-8")
if __name__ == "__main__":
request_my_blog = mock.Mock(return_value="这里是我的播客主页")
print(request_my_blog()) #这里是我的播客主页
实例2:支付接口
支付类payment
import requests
from unittest import mock
class Payment:
"""
定义第三方支付接口,并返回响应状态码
"""
def authe(self, auth, card_num, amount):
'''请求第三方接口,并返回状态码
@param card_num:卡号
@param amount:金额
@param auth: 身份认证
@author: ******
@date: 20210423
@return: 返回状态码,200:支付成功, 500:支付异常 ,401:鉴权失败
'''
url = "https://www.myzf.com"
data = {"card_num": card_num, "amount": amount}
headers = {"Authorzation":auth}
res = requests.post(url, headers=headers, data=data)
return res.status_code
def pay(self, auth, card_num, amount):
"""
支付
:param user_id: 用户id
:param card_num: 卡号
:param amount: 金额
:return:
"""
try:
status_code = self.authe(auth, card_num, amount)
except TimeoutError:
status_code = self.authe(auth, card_num, amount)
if status_code == 200:
return {"result": True}
elif status_code == 401:
return {"result": False, "errorCode":4001, "errorMessage":"Authentication failed" }
elif status_code == 500:
return {"result": False, "errorCode":5000, "errorMessage":"fail" }
测试类test_payment
import unittest
from f.payment import Payment
from unittest import mock
class PaymentTest(unittest.TestCase):
"""
测试支付接口
"""
def setUp(self):
self.payment = Payment()
def test_success(self):
"""
成功场景
"""
self.payment.authe = mock.Mock(return_value=200)
res = self.payment.pay(auth="test", card_num="123456", amount=1000)
self.assertEqual(True, res["result"])
def test_fail(self):
"""
失败场景
"""
self.payment.authe = mock.Mock(return_value=500)
res = self.payment.pay(auth="test", card_num="123456", amount=1000)
self.assertEqual(False, res["result"])
self.assertEqual(5000, res["errorCode"])
self.assertEqual("fail", res["errorMessage"])
def test_auth(self):
"""
鉴权失败
"""
self.payment.authe = mock.Mock(return_value=401)
res = self.payment.pay(auth="error", card_num="123456", amount=1000)
self.assertEqual(False, res["result"])
self.assertEqual(4001, res["errorCode"])
self.assertEqual("Authentication failed", res["errorMessage"])
#side_effect指定异常:
def test_retry_success(self):
"""
测试支付失败重试在成功
side_effect 可以为序列类型 异常类型,对象
如果为序列类型 每次调用mock对象,会依次将side effcet中的元素返回
"""
self.payment.authe = mock.Mock(side_effect=[TimeoutError, 200])
res = self.payment.pay(auth="test", card_num="1234444", amount=1000)
self.assertTrue(res["result"])
if __name__=="__main__":
unittest.main()
2.Fiddler
AutoResponder