mock是针对单元测试的一种应用,用于代替一些不易构造和获取的对象,已达到和真实对象同等的效果,这种应用可以很方便地解除单元测试中的各种依赖,降低编写单元测试的难度,提高工作效率。实现mock的技术可以分为两类:mock数据和mock服务
mock数据:即mock一个对象,写入一些预期值,通过它进行自己想要的测试。常见的有:EasyMock、Mockito、WireMock、JMockit,主要适用于单元测试
mock服务:即mock一个server,构造一个依赖的服务并给予他预期的服务返回值,适合集成测试,如moco框架
mock server是专门实现mock功能的一个服务,模拟其它团队/公司提供的相关服务的临时server
moco是一个类似Mock的工具框架,一个简单搭建模拟服务器的程序库/工具,只需要简单配置request、response等即可满足要求,支持http、https、socket协议,支持在request中设置Headers、Cookies、StatusCode等,支持GET、POST、PUT、DELETE等请求方式;支持多种数据格式,如json、text、xml、file等;可与其它工具集成,如Junit、Maven等
只能模拟出简单的场景。如果接收到请求后需要做一些处理,如需查询数据库、进行运算、或者一些复杂的操作,就无能为力了
步骤一:
下载:https://github.com/dreamhead/moco
下拉找到Quick Start,点击 Standalone Moco Runner
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LIrMOaV3-1644474590739)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209152339691.png)]
步骤二:
安装jdk,配置json文件,将下载好的moco的jar包与配置的json文件放置同一个文件夹路径下,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HreBpON8-1644474590743)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209153248564.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpepzkB2-1644474590744)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209153312224.png)]
在该路径下打开cmd命令行,输入命令:
java -jar moco-runner-1.3.0-standalone.jar http -p 9090 -c test.json
此时相当于mock服务在我们本地9090端口上启动了,可通过浏览器进行访问:localhost:9090/test 访问查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4CaxDLJ-1644474590745)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209154749207.png)]
步骤三:配置不同的请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24DSkpf1-1644474590750)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209161910163.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QSltv2j-1644474590753)(C:\Users\melot\AppData\Roaming\Typora\typora-user-images\image-20220209161517176.png)]
[{
"description":"test demo",
"request":
{
"uri":{
"startswith":"/tes", # 以tes开头
"endsWith":"t", # 以t结尾
"contain":"test" # 包含test
},
"method":"PUT",
"headers":{
"Content-Type":"application/xml"
}
"queries":{
"key1":"abc",
"key2":"123"
}
},
"response":
{
"headers":{
"Content-Type":"application/json"
},
"status":200,
"cookies":{
"login":"true"
}
"text":{
"template":"${req.queries['name']}" # 动态返回参数值
},
"redirectTo":"http://www.baidu.com" # 重定向到百度
"latency":{ # 延迟
"duration":1,
"unit":"second"
}
}
}]
Event事件:请求一些特定接口的时候,需要去请求别的地址,从而才能完成请求。
例如OAuth等,牵涉到第三方的情况
{
"request": {
"uri" : "/event"
},
"response": {
"text": "event"
},
"on": {
"complete": {
"get" : {
"url" : "http://another_site/OAuth?xxx=xxxx"
}
}
}
}
分模块:当我们访问
http://localhost:12306/user/create 和 http://localhost:12306/todo/getAll时候,
会跳到后面对应的json再处理一遍
[
{
"context": "/user", "include": "user.json"
},
{
"context": "/todo", "include": "todo.json"
}
]
//user
[ { "request" : { "uri" : "/create" }, "response" : { "text" : "这是创建用户请求" } } ]
//todo
[ { "request" : { "uri" : "/getAll" }, "response" : { "text" : "这是获取用户所有Todo的请求" } } ]
Moco支持在全局的配置文件中引入其他配置文件,这样就可以分服务定义配置文件,便于管理。
配置好文件,在全局文件中引入即可:
全局配置如下:
java -jar moco-runner-standalone.jar start -p 5638 -g data.json
python3中,mock已经被集成到了unittest单元测试框架中,所以可以直接使用
function.py
def add_and_multiply(x,y):
addition = x + y
multiple = multiply(x,y)
return addition,multiple
def multiply(x,y):
return x * y
func_test.py
import unittest
from unittest import mock
from unittest.mock import patch
from test_mock import function
class MyTestCase(unittest.TestCase):
# 正常用例
def test_add_and_multiply(self):
x = 3
y = 5
addition,multiple = function.add_and_multiply(x,y)
self.assertEqual(8,addition)
self.assertEqual(15,multiple)
# multiply函数不可用时
# patch()装饰/上下文管理区,模拟类或对象在模块测试,在测试过程中,指定的对象将被替换成为一个模拟,并在测试结束时还原
@patch("test_mock.function.multiply")
def test_add_and_multiply2(self,mock_multiply):
x = 3
y = 5
# 设定mock_multiply的返回值固定为15
mock_multiply.return_value = 15
addition,multiply = function.add_and_multiply(x,y)
# 校验mock_multiply方法的参数是否正确
mock_multiply.assert_called_once_with(3,5)
self.assertEqual(8,addition)
self.assertEqual(15,multiply)
# 执行真实函数
def test_add_and_multiply3(self):
x = 3
y = 5
# 若存在side_effect参数值,则该参数值覆盖return_value
function.multiply = mock.Mock(return_value=1,side_effect=function.multiply)
result = function.multiply(3,5)
print(result)
self.assertEqual(result,15)
if __name__ == "__main__":
unittest.main()