PowerMock是一个Mock Server的实现,它同时支持HTTP与gRPC协议接口的Mock,并提供了灵活的插件功能。

这个工具面向于前后端、测试等对有接口Mock需求的开发人员,也可以作为一个通用的Mock服务,部署在网关架构或API管理平台中,实现降级、接口Mock等功能。

项目地址

项目地址:PowerMock

功能

作为一个Mock Server,PowerMock具有以下的核心功能:

  1. 支持 HTTP协议gRPC协议 接口的Mock。
  2. 支持配置 Javascript 等脚本语言来动态生成响应。
  3. 支持对一个接口配置多种响应,并按照条件进行区分。
  4. 匹配条件支持多种运算符(AND/OR/>/</=等)。
  5. 支持返回静态数据以及 特定领域的随机数据
  6. 支持 插件 功能,可以通过编写插件实现其他匹配或Mock引擎。
  7. 同时提供HTTP与gRPC接口,可以动态对MockAPI进行 增删改查
  8. 开箱即用的Redis存储,并支持自由拓展其他存储引擎,比如MySQL、etcd。
  9. 同时支持 windows / darwin / linux 的 32 位 与 64 位。
  10. 语言无关,任何使用HTTP协议或gRPC协议的项目均可以使用本工具。

示例

一、较为高级的用法

本示例可以在 示例代码 找到对应资料

本示例必须使用v8版本的powermock,才能完整支持Javascript的功能

以下面这份配置为示例:

uniqueKey: "advanced_example"
path: "/examples.greeter.api.Greeter/Hello"
method: "POST"
cases:
- condition:
simple:
items:
- operandX: "$request.header.uid"
operator: "<="
operandY: "1000"
response:
simple:
header:
x-unit-id: "3"
x-unit-region: "sh"
trailer:
x-api-version: "1.3.2"
body: |
{"timestamp": "1111", "message": "This message will only be returned when uid <= 1000", "amount": "{{ $mock.price }}"}
- condition:
simple:
items:
- operandX: "$request.header.uid"
operator: ">"
operandY: "1000"
response:
script:
lang: "javascript"
content: |
(function(){
function random(min, max){
return parseInt(Math.random()*(max-min+1)+min,10);
}
return {
code: 0,
header: {
"x-unit-id": (request.header["uid"] % 5).toString(),
"x-unit-region": "bj",
},
trailer: {
"x-api-version": "1.3.2",
},
body: {
timestamp: Math.ceil(new Date().getTime() / 1000),
message: "this message is generated by javascript, your uid is: " + request.header["uid"],
amount: random(0, 5000),
},
}
})()

这份配置定义了一个MockAPI,用于匹配所有路径为 /examples.greeter.api.Greeter/Hello,方法为 POST 的请求,它包含了两个场景,能够实现这样的效果:

1. 条件场景一

当请求 Header 中的 uid <= 1000 时:

  • Response Header 中写入:
x-unit-id: "3"
x-unit-region: "sh"
  • Response Trailer 中写入:
x-api-version: "1.3.2"
  • Response Body 中写入:
{"timestamp": "1111", "message": "This message will only be returned when uid <= 1000", "amount": "{{ $mock.price }}"}

其中的 {{ $mock.price }} 是魔法变量,用于返回一个随机的价格数据。最终,客户端收到的 Response Body 类似于:

{
"timestamp": "1111",
"message": "This message will only be returned when uid <= 1000",
"amount": 7308.4
}

2. 条件场景二

当请求 Header 中的 uid > 1000 时,通过执行以下Javascript脚本返回响应:

(function(){
function random(min, max){
return parseInt(Math.random()*(max-min+1)+min,10);
}
return {
code: 0,
header: {
"x-unit-id": (request.header["uid"] % 5).toString(),
"x-unit-region": "bj",
},
trailer: {
"x-api-version": "1.3.2",
},
body: {
timestamp: Math.ceil(new Date().getTime() / 1000),
message: "this message is generated by javascript, your uid is: " + request.header["uid"],
amount: random(0, 5000),
},
}
})()

在这个脚本中,根据请求的 Header,以及一些内置或自定义函数来生成了响应的code、header、trailer与body。

最终客户端收到的响应体类似于:

{
"timestamp": 1622093545,
"message": "this message is generated by javascript, your uid is: 2233",
"amount": 314
}

它描述了一个相对复杂的场景,当然可能你的需求比较简单,实战的话,我们先从Hello World开始吧!

二、从Hello World开始吧

本示例可以在 示例代码 找到对应资料

首先,创建一个配置文件:

log:
pretty: true
level: debug
grpcmockserver:
enable: true
address: 0.0.0.0:30002
protomanager:
protoimportpaths: [ ]
protodir: ./apis
httpmockserver:
enable: true
address: 0.0.0.0:30003
apimanager:
grpcaddress: 0.0.0.0:30000
httpaddress: 0.0.0.0:30001
pluginregistry: { }
plugin:
simple: { }
grpc: { }
http: { }
script: { }
redis:
enable: false
addr: 127.0.0.1:6379
password: ""
db: 0
prefix: /powermock/

将编译好的PowerMock与上面创建好的配置文件放到同一个目录中,像下面这样:

➜ ls -alh
total 45M
drwxrwxrwx 1 storyicon storyicon 4.0K May 27 14:18 .
drwxrwxrwx 1 storyicon storyicon 4.0K May 24 11:43 ..
-rwxrwxrwx 1 storyicon storyicon 546 May 27 14:16 config.yaml
-rwxrwxrwx 1 storyicon storyicon 45M May 27 14:18 powermock

然后执行

➜ ./powermock serve --config.file config.yaml

如果没有端口冲突的话,你应该已经可以看到服务运行起来了!

1. 先Mock一个HTTP接口

在上面的目录下,创建一个名为 apis.yaml 的文件:

uniqueKey: "hello_example_http"
path: "/hello"
method: "GET"
cases:
- response:
simple:
header:
x-unit-id: "3"
x-unit-region: "sh"
trailer:
x-api-version: "1.3.2"
body: |
hello world!

然后运行:

➜ ./powermock load --address=127.0.0.1:30000 apis.yaml
2:32PM INF start to load file component=main file=load.go:59
2:32PM INF mock apis loaded from file component=main count=1 file=load.go:64
2:32PM INF start to save api component=main file=load.go:76 host= method=GET path=/hello uniqueKey=hello
2:32PM INF succeed! component=main file=load.go:89

这样,我们描述的MockAPI就创建起来了。

通过 curl 或者你的浏览器请求 http://127.0.0.1:30003/hello,可以看到返回给我们 hello world 了!

➜ curl http://127.0.0.1:30003/hello -i
HTTP/1.1 200 OK
Content-Type: application/json
X-Unit-Id: 3
X-Unit-Region: sh
Date: Thu, 27 May 2021 06:36:28 GMT
Content-Length: 12 hello world!

2. 再mock一个gRPC接口

在上面的目录中,创建一个 apis 目录,使整个目录结构像下面这样:

➜  ls -alh
total 45M
drwxrwxrwx 1 storyicon storyicon 4.0K May 27 14:42 .
drwxrwxrwx 1 storyicon storyicon 4.0K May 27 14:37 ..
drwxrwxrwx 1 storyicon storyicon 4.0K May 27 14:23 apis
-rwxrwxrwx 1 storyicon storyicon 1.8K May 27 14:32 apis.yaml
-rwxrwxrwx 1 storyicon storyicon 546 May 27 14:16 config.yaml
-rwxrwxrwx 1 storyicon storyicon 45M May 27 14:18 powermock

在 apis 目录中创建我们的 greeter.proto:

syntax = "proto3";

package examples.greeter.api;
option go_package = "github.com/bilibili-base/powermock/examples/helloWorld/apis;apis"; service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse);
} message HelloRequest {
string message = 2;
} message HelloResponse {
string message = 2;
}

现在整个目录结构像这样:

.
├── apis
│ └── greeter.proto
├── apis.yaml
├── config.yaml
└── powermock

重新运行我们的 powermock 来加载我们新写的proto文件:

➜ ./powermock serve --config.file config.yaml
2:55PM INF starting load proto from: ./apis component=main.gRPCMockServer.protoManager file=service.go:102
2:55PM INF api loaded component=main.gRPCMockServer.protoManager file=service.go:131 name=/examples.greeter.api.Greeter/Hello

在启动日志中可以看到我们新创建的 proto 文件已经被加载到 PowerMock 中了。

将我们的 apis.yaml 文件修改成下面的内容:

uniqueKey: "hello_example_http"
path: "/hello"
method: "GET"
cases:
- response:
simple:
header:
x-unit-id: "3"
x-unit-region: "sh"
trailer:
x-api-version: "1.3.2"
body: |
hello world! --- uniqueKey: "hello_example_gRPC"
path: "/examples.greeter.api.Greeter/Hello"
method: "POST"
cases:
- response:
simple:
header:
x-unit-id: "3"
x-unit-region: "sh"
trailer:
x-api-version: "1.3.2"
body: |
{"message": "hello world!"}

可以看到,里面添加了一个名为 "hello_example_gRPC" 的 MockAPI,我们通过下面的命令装载它:

➜ powermock load --address=127.0.0.1:30000  apis.yaml
3:06PM INF start to load file component=main file=load.go:59
3:06PM INF mock apis loaded from file component=main count=2 file=load.go:64
3:06PM INF start to save api component=main file=load.go:76 host= method=GET path=/hello uniqueKey=hello_example_http
3:06PM INF start to save api component=main file=load.go:76 host= method=POST path=/examples.greeter.api.Greeter/Hello uniqueKey=hello_example_gRPC
3:06PM INF succeed! component=main file=load.go:89

这样,我们的MockAPI就被添加到PowerMock中了。

如果你的环境中有BloomRPC之类的工具的话,可以先通过BloomRPC加载 greeter.proto,然后调用 127.0.0.1:30002

可以看到,调用成功返回了 "hello world"。

如果使用编程语言进行调用的话,以 golang 为例,通过下面的代码调用 PowerMock:

func main() {
fmt.Println("starting call mock server")
conn, err := grpc.Dial("127.0.0.1:30002", grpc.WithInsecure())
if err != nil {
panic(err)
}
client := apis.NewGreeterClient(conn) var header, trailer metadata.MD
startTime := time.Now()
resp, err := client.Hello(context.TODO(), &apis.HelloRequest{
Message: "hi",
}, grpc.Header(&header), grpc.Trailer(&trailer))
if err != nil {
panic(err)
}
fmt.Printf("[elapsed] %d ms \r\n", time.Since(startTime).Milliseconds())
fmt.Printf("[headers] %+v \r\n", header)
fmt.Printf("[trailer] %+v \r\n", trailer)
fmt.Printf("[response] %+v \r\n", resp.String())
}

日志输出是这样的:

starting call mock server
[elapsed] 2 ms
[headers] map[content-type:[application/grpc] x-unit-id:[3] x-unit-region:[sh]]
[trailer] map[x-api-version:[1.3.2]]
[response] message:"This message will only be returned when uid <= 1000"

可以看到,我们的接口被成功Mock出来了!

安装

通过Go安装

安装普通版本,无Javascript支持:

go install github.com/bilibili-base/powermock/cmd/powermock

安装V8版本,支持Javascript:

go install github.com/bilibili-base/powermock/cmd/powermock-v8

开箱即用版本

如果你没有定制插件的需求,开箱即用版本 非常适合你。

通过Makefile编译

如果你是 linux/darwin/wsl 的用户,推荐使用 makefile 来进行安装:

➜ git clone https://github.com/bilibili-base/powermock
➜ cd powermock
➜ make build_linux_v8
➜ make build_linux
➜ make build_darwin
➜ make build_windows

当然也可以直接进行编译:

➜ cd ./cmd/powermock
➜ go install
➜ go build .

PowerMock 支持gRPC的Mock Server实现的更多相关文章

  1. 利用eolinker实现api接口mock测试(mock server)

    转载:http://blog.csdn.net/naicha_qin/article/details/78276172 前后端分离或者是进行单元测试的时候,必须要用mock api替换掉第三方调用或者 ...

  2. Fiddler-009-AutoResponder 简单的 MOCK SERVER 应用实例

    在我们日常的测试中经常需要测试特定的响应对应的客户端展示样式是否正确无误,实现测试方法一般有如下三种: 创建新的测试数据(工作量较大) 修改已有测试数据(例如修改对应的状态码,若是最终需要测试的按钮状 ...

  3. mock server相关解决方案

    前后端分离之后 前后端分离后, 大家从此进入了所谓的并行开发时代. 一旦完成前后端的(边界)分工, 大家就可以各司其职了. 前端在与后端交互时, 要想有效地提高工作效率, 后端的接口文档就是重中之重了 ...

  4. mock server 实现get方法的接口(二)

    mock server 实现get方法的接口(二) 下面是实现查询品牌的接口demo: 1.当response数据量小的时候,可以直接使用json, mock会自动设置headers为applicat ...

  5. 搭建Mock Server

    1.为什么要搭建mock-server? 为了更好的分工合作,让前端能在不依赖后端环境的情况下进行开发,其中一种手段就是为前端开发者提供一个 web 容器,这个本地环境就是 mock-server. ...

  6. Mock Server 之 moco-runner 使用指南二

    文章出处http://blog.csdn.net/crisschan/article/details/53335234 moco-runner 安装配置 1. 下载jar https://repo1. ...

  7. Mock Server 之 moco-runner 使用指南一

    文章出处http://ju.outofmemory.cn/entry/96866 用以下命令可以启动moco-runner 服务 java -jar moco-runner-<version&g ...

  8. 1分钟搭建极简mock server

    1.无聊的背景.起源: 如今的业务系统越来越复杂庞大,各个功能直接的调用也是多如牛毛,但如果在联调的时候,恰好被调的接口正在开发,怎么办?傻傻的等么,不存在的!这时会搭建一些server来进行mock ...

  9. 使用RAP搭建前端Mock Server

    转载自:<前后端分离--构建前端Mock Server--windows部署rap>http://www.cnblogs.com/dothin/p/5361883.html mock:模拟 ...

随机推荐

  1. [其他] vscode 快速教程

    概述 vs:集成开发环境,包括软件生命周期中需要的大部分工具,如UML,代码管控,IDE等 vs code:代码编辑器,支持插件扩展,对网页和云端开发做了优化 快捷键 F1/ctrl+shift+p: ...

  2. Ubuntu20 修改网卡名称

    Ubuntu 修改网卡名称 Ubuntu修改网卡名称 Ubuntu 版本: Ubuntu 20.04 查看当前网卡名称 root@it:~# ip add 1: lo: <LOOPBACK,UP ...

  3. 9.1 ps:查看进程

    ps命令 用于列出执行ps命令的那个时刻的进程快照,就像用手机给进程照了一张照片.如果想要动态地显示进程的信息,就需要使用top命令,该命令类似于把手机切换成录像模式.因为ps命令的功能实在是太多了, ...

  4. mysql-redis连接

    # log 数据库连接 class LogMysql(object): conn = None cursor = None def __init__(self): self.conn = pymysq ...

  5. 书列荐书 |《至关重要的关系》 【美】里德&#183;霍夫曼

    本书的内容不算多,堪称精辟,有些东西甚至可以作为指导思想.括号内为书列君书评. 经典语录: 每个人都是企业家!(否则你无法最大化努力!) 创业和做人是相通的.我们要有计划,要执着,但是也要有弹性,懂得 ...

  6. 降维-基于RDD的API

    降维-基于RDD的API Singular value decomposition (SVD) Performance SVD Example Principal component analysis ...

  7. RGB-D对红外热像仪和毫米波雷达标定

    RGB-D对红外热像仪和毫米波雷达标定 Extrinsic Calibration of Thermal IR Camera and mmWave Radar by Exploiting Depth ...

  8. CVPR2020:点云三维目标跟踪的点对盒网络(P2B)

    CVPR2020:点云三维目标跟踪的点对盒网络(P2B) P2B: Point-to-Box Network for 3D Object Tracking in Point Clouds 代码:htt ...

  9. 配置NFS共享, Linux与Linux的共享

    • Network File System,网络文件系统 – 用途:为客户机提供共享使用的文件夹 – 协议:NFS(TCP/UDP 2049).RPC(TCP/UDP 111) 只读的NFS共享 服务 ...

  10. 基于Android平台的图书管理系统的制作(1)

    在学习了郭神的第一行代码前半段之后,想通过一次实践来完成对已学知识的巩固.于是码下了这个图书管理系统客户端. IDE Android studio,语言 JAVA.XML: 在刚开始设计的时候对于这个 ...