mock server 基础篇

发布时间 2023-09-21 10:40:03作者: 松勤吴老师

1- mock 基础

mock翻译过来是‘模拟’的意思,也就是模拟接口返回的信息,用已有的信息替换接口返回的信息,从而提供仿真环境,实现模拟数据下的功能测试

因为在实际的项目研发过程中,我们经常会遇到如下的尴尬场景:

前端开发依赖于后端接口数据,但是后台人员不足或者无法立即到位,前端迟迟不能开工,或者前端小伙子自己参照ui设计图,完成对应的静态页面(没有数据交互),待后台人员到位,再进行二次开发,协助完成接口对接

2- moco runner 入门

下载

入门案例

  • 编写一个json文件foo.json
[
  {
      "description": "any response",
      "response": {
          "text": "Hello, Moco"
      }
  }
]
  • 运行
java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json
  • 在网页中输入:127.0.0.1:12306能得到如下信息

  • 在控制台会打印如下信息
λ java -jar "moco-runner-1.5.0-standalone.jar" http -p 12306 -c foo.json
18 九月 2023 10:01:20 [main] INFO  Server is started at 12306
18 九月 2023 10:01:21 [main] INFO  Shutdown port is 4726
18 九月 2023 10:01:38 [pool-1-thread-4] INFO  Request received:

GET / HTTP/1.1
content-length: 0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Sec-Fetch-Site: none
Sec-Fetch-Dest: document
Host: 127.0.0.1:12306
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Mode: navigate
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
sec-ch-ua-platform: "Windows"
Sec-Fetch-User: ?1
Accept-Language: zh-CN,zh;q=0.9

18 九月 2023 10:01:38 [pool-1-thread-4] INFO  Response return:

HTTP/1.1 200
Content-Length: 11
Content-Type: text/plain; charset=utf-8

Hello, Moco

18 九月 2023 10:01:39 [pool-1-thread-4] INFO  Request received:

GET /favicon.ico HTTP/1.1
content-length: 0
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Referer: http://127.0.0.1:12306/
Sec-Fetch-Site: same-origin
Sec-Fetch-Dest: image
Host: 127.0.0.1:12306
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Mode: no-cors
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9

18 九月 2023 10:01:39 [pool-1-thread-4] INFO  Response return:

HTTP/1.1 200
Content-Length: 3233
Content-Type: image/png

<content is binary>


  • Moco支持动态加载配置文件,所以无论你是修改还是添加配置文件都是不需要重启服务的

你也可以通过CURL命令行来得到

λ curl 127.0.0.1:12305
Hello, Moco1

命令行

java -jar moco-runner-1.5.0-standalone.jar --help
Moco Options:
moco [server type] -p port -c [configuration file]

server type: http, https, socket
  • 支持三种服务器类型:http、https、socket
  • -p参数指定服务器的端口,你也可以不指定,那就随机分配一个,在console中会显示给你
  • -c参数指定json配置文件

HTTPS 服务器

java -jar moco-runner-<version>-standalone.jar https -p 12306 -c foo.json --https /path/to/cert.jks --cert mocohttps --keystore mocohttps

Socket 服务器

java -jar moco-runner-<version>-standalone.jar socket -p 12306 -c foo.json

多配置文件

java -jar moco-runner-<version>-standalone.jar http -p 12306 -c "*.json"

静默模式

这样你在console中就不会看到那么多的log信息了

java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json -q

查询版本

java -jar moco-runner-<version>-standalone.jar version

全局设置

java -jar moco-runner-<version>-standalone.jar http -p 12306 -g settings.json

环境配置

java -jar moco-runner-<version>-standalone.jar http -p 12306 -g env.json -e remote

env.json

[
    {
        "env" : "remote",
        "include": "foo.json"
    },
    {
        "env" : "local",
        "include": "bar.json"
    }
]

foo.json

[
    {
        "request" : {
            "uri" : "/foo"
        },
        "response" : {
            "text" : "foo"
        }
    }
]

bar.json

[
    {
        "request" : {
            "uri" : "/bar"
        },
        "response" : {
            "text" : "bar"
        }
    }
]

Shutdown

下面的命令定义了一个shutdown port 9527

java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json -s 9527

然后你就可以这样去关闭这个实例

java -jar moco-runner-<version>-standalone.jar shutdown -s 9527

3- moco runner配置参数

类别 配置参数L1 配置参数L2 说明
公共 description 描述
请求 request 请求部分
uri 指定请求资源地址
queries 指定查询参数
method 指定请求方法
forms 指定表单参数
json 指定 JSON 请求体参数
headers 指定请求头
响应 response 响应部分
text 指定响应文本
status 指定响应状态码
headers 指定响应头
cookies 指定响应 cookie
json 指定响应 JSON 数据

① 描述和结构

demo1.json

[
{
  "description":" this is your first demo",
  "request" :
    {
      "uri" : "/foo"
    },
  "response" :
    {
      "text" : "bar"
    }
}
]
curl http://127.0.0.1:12306/foo

响应:bar

注意

  1. description是对这个请求|响应的一个描述
  2. /foo是区分大小写的
  3. curl 127.0.0.1:12305/foo?a=b这样也行
  4. json文件中[]是顶层结构,内部是1个或多个字典
  5. 注意格式,[]内是{},一个{}对应一个请求响应,请求是{"request":""},响应对应
  6. 请求的path是/foo对应的key是uri
  7. 在F12的响应数据中(或log中)你能看到response header中的content-type是text/plain

② request

1. query params

demo2.json

[
{
  "request" :
    {
      "uri" : "/query",
      "queries" : 
        {
          "key" : "value"
        }
    },
  "response" :
    {
      "text" : "query result"
    }
}
]
curl http://127.0.0.1:12305/query?key=value

响应:
query result

注意

  1. 如果你不带参数http://127.0.0.1:12305/query提示出错,如果http://127.0.0.1:12305/query?key=value&name=wuxianfeng,是没问题的
  2. 你可以加参数,如果这样,你至少要提供这2个参数方可,不提供参数或只提供一个都会出错,提供多了是可以的。
      "queries" : 
        {
          "key" : "value",
		  "name": "wuxianfeng"
        }
  1. 实测"key":"value"后如果没值,这个逗号不要轻易写
"key" : "value",

2. http method

  • get
[
{
"request" :
  {
    "method" : "get",
    "uri" : "/get"
  },
"response" :
  {
    "text" : "getfoo"
  }
}
]
curl http://127.0.0.1:12305/get
curl -X GET http://127.0.0.1:12305/get
都能得到getfoo

curl -X POST http://127.0.0.1:12305/get 自然就不行咯

  • post
[
{
"request" :
  {
    "method" : "post",
    "uri" : "/post"
  },
"response" :
  {
    "text" : "postfoo"
  }
}
]
curl -X post http://127.0.0.1:12305/post
得到postfoo
  • 其他方法都是类似的,delete、head、put等等此处不再赘述

3. headers

[
{
  "request" :
    {
      "method" : "post",
	  "uri": "/foo",
      "headers" : 
      {
        "content-type" : "application/json"
      },
	  "json": {
            "foo": "bar"
        }
    },
  "response" :
    {
      "text" : "bar"
    }
}
]

你可以用如下代码来调试

import requests
HOST = 'http://127.0.0.1:12305/foo'
r = requests.post(HOST,json={"foo":"bar"})
print(r.text)

注意

  1. 如果你写成data=是不会有结果的
  2. 如果你写成r.json()是会报错的

4. forms

[
{
  "request" :
    {
      "method" : "post",
	  "uri": "/foo",
      "headers" : 
      {
        "content-type" : "application/x-www-form-urlencoded"
      },
	  "forms": {
            "username": "wuxianfeng",
			"password": "123456"
        }
    },
  "response" :
    {
      "json" : {
	  "code": 200,
	  "msg": "login sucess"
    }
	}
}
]

用下面的代码调试

import requests
HOST = 'http://127.0.0.1:12305/foo'
r = requests.post(HOST,data={"username":"wuxianfeng",
                             "password":"123456"})
print(type(r.text)) # str
print(r.text) #是有结果的
print(type(r.json())) #dict
print(r.json())

[
{
  "request" :
    {
      "uri" : "/cookie",
      "cookies" :
        {
          "path" : "/"
        }
    },
  "response" :
    {
      "text" : "success"
    }
}
]

你可以这样

curl --cookie "path=/" http://127.0.0.1:12307/cookie
# 得到 success

也可以这样

import requests
HOST = 'http://127.0.0.1:12307/cookie'
r = requests.get(HOST,cookies={'path':'/'})
print(r.text)

6. operator

[
{
  "request":
    {
      "uri":
        {
          "startsWith": "/star"
        }
    },
  "response":
    {
      "text": "startsWith"
    }
},
{
  "request":
    {
      "uri":
        {
          "endsWith": "end"
        }
    },
  "response":
    {
      "text": "endsWith  "
    }
},
{
  "request":
    {
      "uri":
        {
          "contain": "con"
        }
    },
  "response":
    {
      "text": "contain  "
    }
}
]

测试

C:\Users\songqin008>curl http://127.0.0.1:12307/stara
startsWith
C:\Users\songqin008>curl http://127.0.0.1:12307/starb
startsWith
C:\Users\songqin008>curl http://127.0.0.1:12307/lend
endsWith
C:\Users\songqin008>curl http://127.0.0.1:12307/acon
contain
C:\Users\songqin008>curl http://127.0.0.1:12307/cona
contain

注意

  1. 你如果写成下面这样,你不能象上面那样访问,/con/end是一个完整的字符串
"contain": "/con"
"endsWith": "/end"
  1. 除了这3个,还有match(正则匹配),exist(存在),path(路径模式)等,详见官方

③ response

1. status code

[
{
  "request" :
    {
      "uri" : "/foo"
    },
  "response" :
    {
	  "text" : "foo status code",
      "status" : 201
    }
}
]

你可以这样

curl http://127.0.0.1:12307/foo
#得到 foo status code

或者这样

import requests
HOST = 'http://127.0.0.1:12307/foo'
r = requests.get(HOST)
print(r.status_code) #得到201

2. redirect

[
{
  "request" :
    {
      "uri" : "/redirect"
    },
  "redirectTo" : "https://www.baidu.com"
}
]

你在地址栏访问http://127.0.0.1/redirect就会跳转到https://www.baidu.com

[
{
  "request" :
    {
      "uri" : "/cookie"
    },
  "response" :
    {
      "cookies" :
      {
        "login" : {
            "value" : "true",
            "domain" : "github.com",
            "path" : "/",
            "secure" : "true",
             "httpOnly" : "true",
            "maxAge": {
                 "duration": 1,
                 "unit": "hour"
             },
            "sameSite": "Lax"
        }
      }
    }
}
]

地址栏访问http://127.0.0.1:12307/cookie

在响应中有如下信息

Set-Cookie:
login=true; Max-Age=3600; Expires=Tue, 19 Sep 2023 07:28:45 GMT; Path=/; Domain=github.com; Secure; HTTPOnly; SameSite=Lax

常见问题

解决中文乱码

java -Dfile.encoding=UTF-8 -jar moco-runner-1.1.0-standalone.jar http -p 9090 -c test.json