# apitest
**Repository Path**: qinxie/apitest
## Basic Information
- **Project Name**: apitest
- **Description**: 一款完备且实用的接口自动化测试框架。支持REST, RPC, SDK等接口形式。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 2
- **Created**: 2021-07-23
- **Last Updated**: 2022-04-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: test-framework, java-sdk-test, cucumber-springboot-allure, gRPC-API-test
## README
English, please click [here](./HELP.md)
# 一款完备实用的API自动化测试框架
## 目录
* 1. REST API的自动化测试
* 1.1 验证所有发布的 API 是否都能工作(冒烟测试)
* 1.2 编写符合实际业务需求的 API 测试用例(功能测试)
* 1.3 构造微服务 mock server,根据不同的请求返回不同的响应指定结果 (集成测试)]
* 2. RPC API的自动化测试
* 2.1 json-RPC API的自动化测试
* 2.2 gRPC API的自动化测试
* 2.3 适用于 json-RPC API 的 mock server
* 2.4 适用于 gRPC API 的 mock server
* 3. SDK自动化测试
```text
微服务时代,API的主要实现方式有 REST, RPC 以及近来比较流行的SDK三种。不同的接口形式,理所当然需要对应不同的测试方法。
```
### 1 REST API的自动化测试
#### 1.1 验证所有发布的 API 是否都能工作(冒烟测试)
##### 测试方法
```text
1. 根据微服务提供的 OPEN API 文件,得到其对外提供的所有 REST API 列表(包括路径、方法、请求消息体结构和接收消息体结构等)
2. 构造请求消息体时:
- 对于简单数据类型(如:integer, number, string, boolean),赋值为此数据类型在此 OPEN API 文件中定义的最大边界值。
- 对于复合数据类型(object 和 array),有明确子数据类型定义的,按定义赋值;无明确子数据类型定义的,统一按string类型处理。
3. 验证响应消息时:
- 验证响应码是否等于 OPEN API 中定义的预期值
- 验证响应消息体结构:
- 简单数据类型仅验证返回的数据类型是否与 OPEN API 文件中预定义的类型匹配,不要求值匹配;
- 复合数据类型 object,需要验证响应消息中是否包含所有的期望 key ,以及匹配 value 的数据类型是否匹配期望;
- 复合数据类型 array,需要验证响应消息中是否包含复合类型 object ?是,则需要验证 key 和数据类型;否则仅需要验证数据类型是否匹配期望。
```
##### 用例参考
- [点击此链接](./resources/features/sanitytest/backend.feature)
- 
##### 优点
```text
- 无需测试人员手动编写测试脚本,测试框架全自动完成 OpenAPI 分析及 REST 消息的发送和结果验证。
- 根据不同微服务的自我定义,自动适配其支持的协议类型(如,http, https, http2等)进行消息的发送和接收。
- 执行速度快,436个 API,请求和验证可以在40秒内完成。
```
##### 局限性
```text
- 未考虑实际的业务需求;仅限于最基本的可行性验证。
- 返回失败的请求需要人工二次校验其准确性(如果事先把测试数据准备好,则可规避此问题,但需要付出更多的人力)。
```
#### 1.2 编写符合实际业务需求的 API 测试用例(功能测试)
##### 测试方法
1. 准备json格式的测试数据,包括:
- 发送请求实时数据
- 接收响应实时数据 [格式参考](./resources/testdatacollection/v2.2.0/runtime/user_management/UserController.json)
- (可选)需要预先存储到数据库的准备数据 [格式参考](./resources/testdatacollection/v2.1.0/testdata/orgUser.json)
注意:
- 用于REST消息发送/接收的json数据文件应保留所有的域。某个域若无值,则填写null。
- 数据库预处理json数据文件无特别要求。
2. 编写脚本将请求数据发送给指定目的微服务。
3. 测试框架根据测试人员提供的接收响应数据,进行消息结果验证:
- 符合预期,则测试通过;
- 否则打印错误断言,测试退出并标记为失败。
##### 用例参考
- [点击此链接](./resources/features/apitest/restful/user_management/user_register.feature)
##### 优点
```text
- 用例编写简单,核心测试脚本仅数行。
- 消息的发送、接收以及结果验证对用例编写人员透明。测试用例编写人员需要关心的只是产品业务需求。
- 测试数据与功能需求关联,支持复用。不同测试脚本(用例)可使用相同测试数据。
```
##### 局限性
```text
- 更多需要讨论的是 API 测试本身的局限性,和本测试框架无关。
```
#### 1.3 构造微服务 mock server,根据不同的请求返回不同的响应指定结果 (集成测试)
##### 测试方法
```text
mock server既可以与具体的测试用例集成,也可以被部署为一个真实服务供客户端服务(在开发中的测试服务对象)调用;
```
作为服务单独部署:
1. 当模拟为一个单独的服务,则修改[application.yml](./src/main/resources/application.yml)中参数 serviceMock.mocker,赋值为某一个具体的服务名称;
若希望在一个服务器中模拟所有服务,则修改上述参数的值为allRestInOne;
2. 准备json格式的请求和响应数据,存放在servicemocks目录,文件名与模拟的服务名保持一致;
3. 执行命令行命令 gradlew build,将生成的 jar 拷贝到目的服务器,或者通过 Dockerfile 生成镜像;
4. 启动命令:java -jar auto-test-1.0.0-SNAPSHOT.jar --server.ssl.enabled=<> --server.port=<>
- 参数1:是否启用https,默认为false。
- 参数2:服务端侦听端口,默认为8080。
5. 工作原理说明:
- mock server每收到一条 REST 请求,就会读取一次 json 格式的测试数据一次;
- mock server将逐一匹配请求数据的每一个域,最后返回与请求数据匹配度最高的某个测试数据中response域的值;
- 即,不同的两个测试数据,可以仅有一个域的值不同;mock server进行精确匹配,返回匹配成功的那个数据的response域值;
- 如果找不到合适的测试数据进行匹配,则返回失败码400(bad request);
- [数据格式参考](./servicemocks/backend.json)
与测试用例集成:
1. 准备json格式的请求和响应数据,存放在servicemocks目录,文件名与模拟的服务名保持一致;
2. 启动测试;
3. 待mock server收到请求并返回响应后(可通过在测试用例中添加定时器实现等待),通过时间戳进行交易日志(路径:/target/cucumber/log/autotest_custom.log)过滤,判断是否已收到请求和请求是否与期望一致。
##### 实现参考
[点击链接](./src/main/java/pers/qinxie/autotest/servicemock/rest/RestMockController.java)
##### 优点
```text
1. 测试数据支持动态修改,无需重启服务。
2. 返回结果支持参数化,即可以根据请求路径中的参数,动态替换返回结果中对应的参数的值。
3. 适配客户端http/https请求模式并响应;HTTP协议支持HTTP1.X和HTTP2, HTTP2同时支持h2和h2c。
- 如何在本机生成证书?使用命令: keytool -genkey -alias netty-reactor -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
- 相关参数应根据在application.properties内的定义进行修改
- curl命令验证例子: curl -H "Content-Type:application/json" -X POST -d '{"user_id": "123", "coin":100, "success":1, "msg":"OK!" }' -v --http2-prior-knowledge http://172.22.17.74:8080/userservice/fedFromOrg/87654321
```
##### 持续改进
```text
1. 需要支持插件等形式,对响应数据进行用户/请求差别化地动态修改(如,变量替换,实时算法调用等)
```
### 2 RPC API的自动化测试
```text
RPC形式的API,又以 gRPC 和 json-RPC 两种实现方式居多。
```
#### 2.1 [json-RPC](https://www.jsonrpc.org/specification) API的自动化测试
```text
从 json-RPC 的规范我们可以知道,相比较于 REST ,jsonRPC 消息有如下几个特点:
- 净荷消息由 HTTP1.1 承载;
- 请求方法仅有 POST 一种;
- 请求消息头(Header)中 Content-Type 必须是 application/json;
- 请求消息体(Request Body)中必须包含 json-RPC 的关键字属性:
- jsonrpc = "2.0"
- id = 2
- method = [rpc method]
由此可见,json-RPC API的测试方法,和 REST API 的测试方法基本相同。
```
#### 2.2 [gRPC](https://grpc.io/) API的自动化测试
##### 测试方法
1. 获取 gRPC 接口定义的 [proto](https://developers.google.com/protocol-buffers/docs/proto3)文件,存放在对应测试版本的测试数据文件夹下([参考存放路径](/src/test/resources/testdatacollection/chain33/))。
- 根据实际情况判断(如,存在重名类等)是否需要在 proto 文件中指定输出的 java 类名(option java_outer_classname = "RpcProto";)
- 需要生成service类(option java_generic_services = true;)
2. 通过 gradle 插件命令生成java class。可携带参数chainVer,表示proto文件版本。
- gradlew generateProto -DchainVer=v1.6.6
3. 编写 gRPC 客户端请求数据文件([参考链接](./resources/testdatacollection/v2.2.0/runtime/chain_ops/sendTransaction.json))
3. 由于 gRPC 接口对负载净荷数据有其自定义的规范要求,所以只能手工实现请求数据的类型转换,然后发送和响应数据的验证:
- 编写 gRPC 客户端发送程序代码(主要是把步骤3中的数据,序列化成2进制码流。[参考链接](./src/main/java/com/fuzamei/autotest/rpc/grpc/GrpcClientTransaction.java))
- 编写 gRPC 服务端响应数据验证代码(主要是和 proto 文件中定义的 service.returns 进行结果比对)。
##### 用例参考
- [点击此链接](./resources/features/apitest/grpc/accountManager.feature)
##### 优点
```text
- N/A
```
##### 持续改进
```text
- 对于语法不完整的 proto 文件,无法实现自动化验证;
- 相对 RESTful 接口自动化测试用例来说,RPC接口自动化测试用例需要更多的代码编写量;
- 需要找寻一个通用的解决方案去实现:测试数据与实际发送数据类型的自动转换机制。
```
#### 2.3 适用于 json-RPC API 的 mock server
[参考 1.1.3 实现](#a-idchapter113113--mock-server-a)
#### 2.4 适用于 gRPC API 的 mock server
##### 测试方法
与 [1.1.3](#a-idchapter113113--mock-server-a) 方法大体相同,但仍然存在如下区别:
```text
1. 因为 protobuf 协议的差异性,必须手动编写proto文件中service定义的实现类。
2. gRPC服务端默认侦听端口为8802,可通过传入参数 --grpc.server.port=<> 来指定。
```
##### 实现参考
[点击链接](./src/main/java/pers/qinxie/autotest/servicemock/grpc/GrpcServerService.java)
##### 优点
```text
1. REST API和 gRPC API的mock server可部署在同一台机器,同时启动且不互相冲突(前提是保证侦听端口不一样)。
```
##### 持续改进
```text
需要找寻一个通用的解决方案去实现:测试数据与实际发送数据类型的自动转换机制。从而减少业务代码的编写,测试人员需要关心的只是测试数据和业务流程验证。
```
## 3. SDK自动化测试
```text
SDK(Software Development Kit)是为第三方开发者提供的软件开发工具包,包括SDK接口、开发文档和Demo示例等。
SDK自动化测试目标性很强,一般都是为了验证某些个特定业务场景的测试。所以很难抽象出一些公共或通用方法去处理这类的测试。
```
##### 用例参考
- [点击此链接](./resources/features/sdktest/storage.feature)