1.1.17 • Published 3 years ago
zhl-api-test v1.1.17
功能
- 尽量简单,不要有复杂的依赖;
- HTTP API 测试;
- 因为 API 之间有依赖关系,所以要能按照依赖关系进行执行;
- 某些 API 需要从新的 Session 开始执行,需要满足可以设定 API 测试线从新的 Session 执行;
- API 可以多次循环测试;
- API 线可以多次循环测试;
- 方便的开启 debug 功能;
使用方法
作者习惯使用 Spring Boot 搭建服务,故使用 Spring Boot 进行说明。
假设 Spring Boot 项目已经搭建好,现在需要开始 API 测试,目录结构如下:
spring-boot-project
|---- ...
|---- ...
|---- ...
导入框架
打开 CMD,cd 到 spring-boot-project
工程目录下。
新建 node 项目,如 api-test
创建目录 api-test
mkdir api-test
cd api-test
npm 初始化 node 项目
npm init
然后根据提示填写信息,不清楚的话就一路回车。
添加依赖库
打开 api-test 目录下 package.json 文件,修改如下:
{
"name": "api-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
// 添加这几行
"dependencies": {
"zhl-api-test": "^1.1.12"
}
}
安装依赖
npm i
等待执行完成。
导入框架完成
此时目录结构:
spring-boot-project
|---- ...
|---- ...
|---- ...
|---- api-test
|---- node_modules
|---- ...
|---- ...
|---- ...
|---- package.json
|---- package-lock.json
|---- ...
添加测试单元
在 api-test 目录中,添加 index.js 文件。文件内容如下:
const {StartApiTest} = require('zhl-api-test')
// 正确的请求测试
const TEST_CORRECT_REQUEST = {
'Hello 测试': {
dependentItem: [],
requestConfig: {
method: 'post',
url: '/say/hello'
},
assert: (data) => {
console.assert(null != data, "返回数据不为空");
},
},
}
// 错误的请求测试
const TEST_WRONG_REQUEST = {
}
const TEST_LIST = Object.assign({}, TEST_CORRECT_REQUEST, TEST_WRONG_REQUEST);
// 进行测试
StartApiTest(TEST_LIST);
注:也可在 api-test 中建目录,分模块进行测试。
创建自动化测试流程
在 Spring Boot 中,一般是在 src/test 目录中的 ...ApplicationTests
类中定义测试。
package ...;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class ApplicationTests {
public static final String NODE_ACTUATOR =
"node 可执行文件位置,Windows 中为 node.exe,Linux 中为 node";
private static final List<String> outputString = new CopyOnWriteArrayList<>();
@LocalServerPort
private int port;
private void printMessage(final InputStream input) {
new Thread(() -> {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
String line;
try {
while ((line = bufferedReader.readLine()) != null) {
outputString.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
@Test
void apiTest() {
System.out.println("port = " + port);
try {
File workDir = new File("./api-test");
Process process = Runtime.getRuntime().exec(NODE_ACTUATOR + " index.js http://127.0.0.1:" + port, null, workDir);
printMessage(process.getErrorStream());
printMessage(process.getInputStream());
int exitVal = process.waitFor();
System.out.println("\n\n===============================================================================\n\n");
System.out.println("node test finished, Exit code is " + exitVal);
AtomicBoolean noErrorMessage = new AtomicBoolean(true);
outputString.forEach(line -> {
if (null == line) {
return;
}
String lineLowerCase = line.trim().toLowerCase();
if (lineLowerCase.contains("error:")
|| lineLowerCase.contains("assertion failed")) {
System.err.println(line);
if (noErrorMessage.get()) {
noErrorMessage.set(false);
}
} else {
System.out.println(line);
}
});
assert exitVal == 0;
assert noErrorMessage.get();
System.out.println("\n\n===============================================================================\n\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时,便完成了自动化测试。
测试单元规则详解
单个测试:
// 这是一条测试
const TestNodeTemplate = {
// String,测试名称,可为空
// 在测试组中作为键值定义即可
name: '',
// Array<String>,必须定义,所依赖的测试,比如,登录前,需要先请求得到验证码,那么登录测试就得依赖于请求验证码测试
dependentItem: [],
// Object or Function,必须定义,可以是静态的 Object,也可以定义函数,注:如是函数,则每次请求都会调用函数
// requestConfig 具体内容参照 AxiosRequestConfig,必须要定义的有 url,method
requestConfig: {},
// Function or Array<Function>,可为空,数据通过函数或函数数组提供
// 如果是函数数组,则 cycle 值会被设置为 data.length
data: () => {
},
data: [() => {}, () => {}, () => {}, ....]
// 'parallel' or 'serial',定义测试是并行执行还是串行执行。默认 'serial'
parallelOrSerial: 'serial',
// Function,必须定义,对返回内容数据的断言函数
assert: (data, requestConfig) => {
// 如:
console.assert(null != data);
},
// Function,对返回数据的断言函数,默认空函数
assertResponse: (response, requestConfig) => {
// 如:
console.assert(null != response);
},
// Integer,测试自身循环次数,默认为 1
// 如果 data 是函数数组,则 cycle 值会被设置为 cycle × data.length
cycles: 1,
// 循环结束函数。默认为一个返回 true 的函数,
// 如果该设置了该函数,则 cycle 的值会被重新设置,如果 data 是函数,则 cycle 设置为 1,如果是 函数数组,则设置为 data.length
// 注:并行执行不支持 requestConfig 函数,且 response 的结果不可预期
// 有些测试需要判断数据是否符合一定条件,才会结束循环
loopEndFunction: (response, requestConfig) => {
return true;
}
// 延迟执行,默认为 0,不延迟
delay: 0,
// Integer,测试线循环次数,默认为 1
// 该定义必须定义在没有任何测试依赖的测试上(即测试线最末尾的测试)
lineCycles: 1,
// true or false,是否需要在新 Session 中进行该测试,默认 false
newSession: false,
// Function,回调函数,可做一些请求后的处理工作,默认空函数
callback: (response, requestConfig) => {
}
}
测试线:
const TEST_CORRECT_REQUEST = {
// 定义单个测试,测试名作为键值
'Hello 测试': {
dependentItem: [],
requestConfig: {
method: 'post',
url: '/say/hello'
},
assert: (data) => {
console.assert(null != data, "返回数据不为空");
},
},
}
依赖规则
假设有如下测试依赖:
A 依赖 B
B 依赖 C、D、E
C 依赖 F
D、F 依赖 G
E 依赖 H
如图示:
A--->B--->C--->F--->G
|--->D---------↑
|
|--->E-------->H
则有两条测试线,分别是以 G 和 H 开始的测试线,测试单元执行过程如下:
// 即 B 一定先于 A 执行,(C、F)组合与 D 可以并行执行,无执行顺序要求(这里展示的是串行执行过程,所以只是其中一种情况),
// 但 C、D 一定先于 B 执行,F 一定先于 C 执行,当然,最初执行肯定是 G
G --> D --> F --> C --> B --> A
// H 的这条线只需跟着依赖走即可
H --> E --> B --> A
原则就是:测试单元的依赖一定被全部执行完成后才会执行测试单元本身。
1.1.17
3 years ago
1.1.16
3 years ago
1.1.15
3 years ago
1.1.14
3 years ago
1.1.13
3 years ago
1.1.12
3 years ago
1.1.11
3 years ago
1.1.10
3 years ago
1.1.9
4 years ago
1.1.8
4 years ago
1.1.7
4 years ago
1.1.6
4 years ago
1.1.5
4 years ago
1.1.3
4 years ago
1.1.2
4 years ago
1.1.1
4 years ago
1.1.0
4 years ago
1.0.1
4 years ago
1.0.0
4 years ago