Mock是一个零侵入的服务端Mock平台,底层基于JVM Sandbox。相比于Fiddler、Charles和Burp Suite等客户端的代理调试工具,Mock的优势在于可以对调用链中的任何服务节点数据进行Mock,而不只是Mock离客户端最近一个节点的数据。
- 零侵入,即不修改目标应用程序任何代码就能实现Mock功能。
- 支持静态Mock,即根据配置的类和方法,返回Mock数据。
- 支持动态Mock,即根据调用方法时传入的参数,动态判断是返回Mock数据还是真实数据。
- 支持返回多种Mock数据,包括JSON列表、JSON、null、String和基本类型,并支持抛出异常。
- 新增、编辑、删除、启用和禁用配置时,不需要重启目标应用程序。
- 自带Mock控制台,可通过界面管理Mock配置和查看目标应用程序。
- 自带示例应用程序,可作为目标应用程序以便用户学习。
- Mock模块:即本工程中的mock-module。Mock模块的底层基于JVM Sandbox,其属于JVM Sandbox的一个用户模块,可被JVM Sandbox加载。由于JVM Sandbox只支持Linux、Unix和macOS操作系统,因此Mock模块也只能在这些操作系统上安装。
- Mock控制台后端:即本工程中的mock-be。它是Mock控制台的后端服务,使用MySQL作为数据库。
- Mock控制台前端:即mock-fe工程。它是Mock控制台的前端服务。
- 示例应用程序:即本工程中的mock-example-app。示例应用程序提供多个示例接口,以便用户学习使用。
由于安装过程会用到一些基础工具(比如wget),若提示“未找到命令”或“command not
found”,则需要先安装对应的基础工具。
演示安装过程的服务器IP地址为192.168.3.102,用户需根据实际情况进行修改。
有两种方法可以安装Mock模块,任选其一。
下载在线安装脚本:
wget https://github.com/lujiatao2/mock/releases/download/1.0.0/install-online.sh
给安装脚本增加执行权限:
chmod +x install-online.sh
执行安装脚本:
./install-online.sh
下载源代码到本地:
git clone https://github.com/lujiatao2/mock.git
进入安装脚本所在目录:
cd mock/bin/
给安装脚本增加执行权限:
chmod +x install-local.sh
执行安装脚本:
./install-local.sh
由于Mock控制台使用的是常用技术栈,因此安装方式很多,这里仅以Docker来演示安装过程,容器管理使用Docker
Compose(当然也可以使用Docker Swarm或Kubernetes等)。
新增docker-compose.yml文件,文件内容如下:
version: "3.3"
services:
mock-mysql:
image: mysql:5.7.33
container_name: mock-mysql
restart: always
ports:
- "10000:3306"
environment:
- MYSQL_ROOT_HOST=%
- MYSQL_ROOT_PASSWORD=root123456
- MYSQL_DATABASE=mock
- TZ=Asia/Shanghai
command: --character-set-server=utf8
volumes:
- /opt/mysql/mock:/var/lib/mysql
mock-be:
image: registry.cn-chengdu.aliyuncs.com/lujiatao/mock-be:1.0.0
container_name: mock-be
restart: always
ports:
- "10001:8080"
environment:
- MYSQL_URL=jdbc:mysql://mock-mysql:3306/mock
depends_on:
- mock-mysql
mock-fe:
image: registry.cn-chengdu.aliyuncs.com/lujiatao/mock-fe:1.0.0
container_name: mock-fe
restart: always
ports:
- "10002:80"
environment:
- MOCK_BE_IP=192.168.3.102
- MOCK_BE_PORT=10001
- MOCK_FE_PORT=10002
depends_on:
- mock-be
可能需要修改的配置:
- MySQL、Mock控制台后端和Mock控制台前端映射的宿主机端口分别为10000、10001和10002,需根据实际情况进行修改。
- MySQL root密码默认为root123456,需根据实际情况进行修改。
- Mock控制台后端IP地址默认为192.168.3.102,需根据实际情况进行修改。
执行以下命令启动Mock控制台:
docker-compose up -d
执行以下命令查看Mock控制台运行状态:
docker-compose ps
如果启动成功,那么mock-be、mock-fe和mock-mysql服务都处于Up状态,如下所示:
Name Command State Ports
----------------------------------------------------------------------------------------
mock-be java -jar mock-be.jar Up 0.0.0.0:10001->8080/tcp
mock-fe /docker-entrypoint.sh /bin ... Up 0.0.0.0:10002->80/tcp
mock-mysql docker-entrypoint.sh --cha ... Up 0.0.0.0:10000->3306/tcp, 33060/tcp
访问 http://192.168.3.102:10002/ 显示Mock控制台首页,如下图所示:
在对目标应用程序进行Mock之前,需求做一些配置工作。
为了方便用户学习,这里提供了一个示例应用程序作为待Mock的目标应用程序。
下载示例应用程序:
wget https://github.com/lujiatao2/mock/releases/download/1.0.0/mock-example-app-1.0.0.jar
执行以下命令启动示例应用程序:
java -jar mock-example-app-1.0.0.jar
由于示例应用程序默认使用8080端口,如果与现有端口冲突,可指定不同的端口号来启动,比如指定8090端口启动:
java -jar mock-example-app-1.0.0.jar --server.port=8090
启动成功后就可以调用示例应用程序的接口了:
curl http://192.168.3.102:8090/mock-example-app/by-id?id=1
接口返回如下:
{
"id": 1,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
示例应用程序接口详见5.1 示例应用程序接口。
Mock模块的配置文件是mock.properties,位于~/.sandbox-module/cfg目录,配置项说明如下:
- app.env:Mock应用环境,比如development、test、demo、production等,需根据实际情况进行修改,缺省值是unknown。
- app.name:Mock应用名称,比如order-server、pay-server等,需根据实际情况进行修改,缺省值是unknown。
- mock.be.url:Mock控制台后端URL地址,需根据实际情况进行修改,缺省值是 http://127.0.0.1:80 。
以下是演示使用的配置:
# Mock应用环境
app.env=demo
# Mock应用名称
app.name=mock-example-app
# Mock后端URL
mock.be.url=http://192.168.3.102:10001
接入Mock应用是指使用JVM Sandbox加载Mock模块生成Mock模块的实例,并将Mock模块实例注册到Mock控制台。
进入JVM Sandbox脚本所在目录:
cd ~/sandbox/bin/
执行以下命令接入Mock应用:
./sandbox.sh -p `ps -ef | grep mock-example-app-1.0.0.jar | grep -v grep | awk '{print $2}'`
-p命令指定的是目标应用程序的进程号,这里使用了ps命令,并结合grep和awk命令查询进程号。
访问 http://192.168.3.102:10002/mock-app ,可以看到目标应用程序已经接入了Mock控制台:
点击查看日志可以看到当前Mock模块实例的日志:
首先新增一个Mock配置:
以上配置将/mock-example-app/by-id接口的所有请求都返回以下硬编码的数据:
{
"id": 999,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
执行以下命令调用/mock-example-app/by-id接口:
curl http://192.168.3.102:8090/mock-example-app/by-id?id=1
此时返回值的ID等于1,并不是999(即并不是配置的Mock数据)。Mock配置没有生效的原因是Mock模块实例没有重新加载配置,解决方法是手动点击刷新配置即可:
除了新增Mock配置,编辑、删除、启用和禁用Mock配置时,都需要手动刷新配置才能生效。
重新执行以下命令:
curl http://192.168.3.102:8090/mock-example-app/by-id?id=1
此时返回值的ID等于999,即返回值是配置的Mock数据。
在调用/mock-example-app/by-id接口时,即使把id换成2、3、4、5、6,返回值仍然是配置的Mock数据,因此这种Mock配置是静态的。
如果根据调用方法时传入的参数,动态判断是返回Mock数据还是真实数据,这就是动态Mock。
当id大于等于5时返回Mock数据,小于5时返回真实数据,可以这样来配置:
手动刷新配置后,执行以下命令:
curl http://192.168.3.102:8090/mock-example-app/by-id?id=5
返回值的ID等于999:
{
"id": 999,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
执行以下命令:
curl http://192.168.3.102:8090/mock-example-app/by-id?id=4
返回值的ID等于4:
{
"id": 4,
"stringParameter": "String Parameter 3",
"intParameter": 3,
"integerParameter": 13,
"doubleParameter": 0.3,
"booleanParameter": false,
"adoubleParameter": 1.3,
"abooleanParameter": true
}
如果一个方法有多个参数,比如/mock-example-app/by-conditions接口对应的getByConditions方法就有多个参数,接口调用方式如下:
curl "http://192.168.3.102:8090/mock-example-app/by-conditions?stringParameter=String+Parameter+0&&intParameter=0&&integerParameter=10&&doubleParameter=0.0&&aDoubleParameter=1.0&&booleanParameter=true&&aBooleanParameter=false"
真实返回值如下:
[
{
"id": 1,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
]
如果只想匹配intParameter等于0,那么可以将其他参数配成忽略:
Mock数据填null:
Mock数据直接填异常的全名:
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getAll(),接口返回值如下:
[
{
"id": 1,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
},
{
"id": 2,
"stringParameter": "String Parameter 1",
"intParameter": 1,
"integerParameter": 11,
"doubleParameter": 0.1,
"booleanParameter": false,
"adoubleParameter": 1.1,
"abooleanParameter": true
},
{
"id": 3,
"stringParameter": "String Parameter 2",
"intParameter": 2,
"integerParameter": 12,
"doubleParameter": 0.2,
"booleanParameter": true,
"adoubleParameter": 1.2,
"abooleanParameter": false
},
{
"id": 4,
"stringParameter": "String Parameter 3",
"intParameter": 3,
"integerParameter": 13,
"doubleParameter": 0.3,
"booleanParameter": false,
"adoubleParameter": 1.3,
"abooleanParameter": true
},
{
"id": 5,
"stringParameter": "String Parameter 4",
"intParameter": 4,
"integerParameter": 14,
"doubleParameter": 0.4,
"booleanParameter": true,
"adoubleParameter": 1.4,
"abooleanParameter": false
},
{
"id": 6,
"stringParameter": "String Parameter 5",
"intParameter": 5,
"integerParameter": 15,
"doubleParameter": 0.5,
"booleanParameter": false,
"adoubleParameter": 1.5,
"abooleanParameter": true
},
{
"id": 7,
"stringParameter": "String Parameter 6",
"intParameter": 6,
"integerParameter": 16,
"doubleParameter": 0.6,
"booleanParameter": true,
"adoubleParameter": 1.6,
"abooleanParameter": false
},
{
"id": 8,
"stringParameter": "String Parameter 7",
"intParameter": 7,
"integerParameter": 17,
"doubleParameter": 0.7,
"booleanParameter": false,
"adoubleParameter": 1.7,
"abooleanParameter": true
},
{
"id": 9,
"stringParameter": "String Parameter 8",
"intParameter": 8,
"integerParameter": 18,
"doubleParameter": 0.8,
"booleanParameter": true,
"adoubleParameter": 1.8,
"abooleanParameter": false
},
{
"id": 10,
"stringParameter": "String Parameter 9",
"intParameter": 9,
"integerParameter": 19,
"doubleParameter": 0.9,
"booleanParameter": false,
"adoubleParameter": 1.9,
"abooleanParameter": true
}
]
5.1.2 /mock-example-app/by-conditions?stringParameter=String+Parameter+0&&intParameter=0&&integerParameter=10&&doubleParameter=0.0&&aDoubleParameter=1.0&&booleanParameter=true&&aBooleanParameter=false
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getByConditions(String stringParameter, int intParameter, Integer integerParameter, double doubleParameter, Double aDoubleParameter, boolean booleanParameter, Boolean aBooleanParameter),接口返回值如下:
[
{
"id": 1,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
]
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getById(int id),接口返回值如下:
{
"id": 1,
"stringParameter": "String Parameter 0",
"intParameter": 0,
"integerParameter": 10,
"doubleParameter": 0.0,
"booleanParameter": true,
"adoubleParameter": 1.0,
"abooleanParameter": false
}
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getString(),接口返回值如下:
我是String
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getByte(),接口返回值如下:
1
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getAByte(),接口返回值如下:
2
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getShort(),接口返回值如下:
3
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getAShort(),接口返回值如下:
4
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getInt(),接口返回值如下:
5
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getInteger(),接口返回值如下:
6
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getLong(),接口返回值如下:
7
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getALong(),接口返回值如下:
8
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getFloat(),接口返回值如下:
9.0
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getAFloat(),接口返回值如下:
10.0
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getDouble(),接口返回值如下:
11.0
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getADouble(),接口返回值如下:
12.0
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getBoolean(),接口返回值如下:
true
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getABoolean(),接口返回值如下:
false
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getChar(),接口返回值如下:
"a"
对应的方法为com.lujiatao.mock.example.app.MockExampleAppController#getCharacter(),接口返回值如下:
"b"
针对想对本项目做贡献的开发者,有几点需要注意:
- Mock模块使用纯Java开发,确保JDK版本不低于JDK 8。
- Mock控制台后端使用Spring Boot + MyBatis + MySQL开发,确保JDK版本不低于JDK 8,以及MySQL数据库已安装。服务默认端口为8080,需根据实际情况进行修改。
- Mock控制台前端使用Node.js + Vue.js + Element Plus开发,启动前请确保Mock控制台后端服务已启动。
- 示例应用程序使用Spring Boot开发,确保JDK版本不低于JDK 8。服务默认端口为8080,需根据实际情况进行修改。