测试开发之接口篇-使用K6完成接口自动化测试
原创- 2023-05-10 09:30:00
- 1949
K6是一个优秀的开源性能测试工具,它提供了简洁又丰富的API,灵活和易用的描述性语法。以下列出K6中的几个概念:
- Metrics:测试度量的指标。如请求响应时间、执行耗时、检查点通过率、失败次数等;
- Thresholds:定义了指标度量的成功、失败标准。如请求失败率小于5%,迭代执行耗时平均小于3秒;
- Checks:测试用例的检查点。如响应状态码等于200,响应内容包括文本关键字等;
- Tags:可作用于请求、指标、阀值和检查点的标签,便于分类进行统计;
- Groups:以函数的形式对脚本进行逻辑划分,以方便进行分类统计分析;
- Scenarios:用于控制性能测试中的加压方式和过程,如虚拟用户数量和加载、并发集合点、迭代模式等。
本文针对接口测试的目标,需要我们在脚本中实现以下的控制处理,这里不涉及更多的性能测试的有关内容。
- 控制并发用户数和接口调用次数;
- 验证指定编号用例的请求响应时间平均值小于6秒;
- 验证某个分组中请求的响应时间90%小于6秒;
- 验证某个分组整体脚本执行时间平均值小于6秒;
- 使用多级分组模拟测试套件及其子套件;
- 支持在检查点配置测试用例编号和名称;
- 输出JSON格式的测试结果摘要和日志文件。
下面我们看一下示例的脚本,完整的脚本文件请访问GitHub。
import http from 'k6/http'; import exec from 'k6/execution'; import { check, sleep, group } from "k6";
export const options = {
// 设置并发用户数及其加载方式
stages: [
{ duration: "2s", target: 3 }, // 2秒内,加载3个虚拟用户
{ duration: "1s", target: 0 }, // 1秒内,销毁所有虚拟用户
],
// 设置性能有关指标的阀值,形如{id:1}标签的指标会影响指定用例的成败。
thresholds: {
// 验证编号为1的用例的响应时间,平均值小于1000毫秒 'http_req_duration{id:1}': ['avg < 1'],
// '用户登录'分组中所有请求的响应时间,90%小于6000毫秒 'http_req_duration{group:::登录请求}': ['p(90) < 6000'],
// '用户登录'分组的整体执行时间,平均值小于6000毫秒 'group_duration{group:::登录请求}': ['avg < 6000'],
},
};
export default function () {
// 单元测试套件 group('用户登录', function () {
let resp = http.get('https://httpbin.org/get?p1=1', {
tags: { id: '1' }, // 标记用例编号为1,用于上述阀值统计
});
// 期待响应状态码
let expectRespStatus = 200 // 通过设置错误的期待结果,模拟三分之一的迭代失败
// if (+`${exec.vu.idInTest}` % 3 === 0) {
// expectRespStatus = 222 // }
// console.log(`in iteration ${exec.vu.idInTest}, expectRespStatus=${expectRespStatus}`)
// 验证器方法,可以验证响应的状态码、耗时、内容等
const validator = (r) => resp.status == expectRespStatus
// 注意:此处的检查点和前面定义的阀值'http_req_duration{id:1}'均会影响用例的成败
// 断言方法(用例ID, 用例名称,验证点名称,被验证数据,验证器) assert(1, '微信扫码登录', '验证跳转到个人仪表盘', resp, validator);
// 子套件'用户登录-登录次数限制'及其下用例代码 group('失败次数限制', function () { assert(0, '登录失败连续3次,账号锁定', '显示账号已被锁定', resp, (r) => true); assert(0, '登录非连续性失败累计达到3次,账号不锁定', '可成功登录', resp, (r) => true); assert(0, '锁定账号15分钟后自动解禁', '解禁后可成功登录', resp, (r) => true);
});
// 当前套件'用户登录'下的另一个用例 group('CASE', function () { assert(0, '邮箱登录', '验证到达首页', resp, (r) => resp.status == 200);
});
sleep(1);
}); group('用户管理', function () { // 单元测试套件
const resp = http.post('https://httpbin.org/post', JSON.stringify({
foo: 'abc',
bar: '123',
}), {
tags: { foo: 'bar' },
headers: { 'Content-Type': 'application/json',
},
});
// 验证器
const validator = (data) => {
const status = data.status
const dur = data.timings.duration
// console.log('===', status, dur)
// 验证状态码和响应时间
const pass = status == 200 && dur < 6000 return pass
}
// 断言:用例ID, 用例名称,验证点名称,被验证数据,验证器 assert(0, '重置密码', '验证用户收到密码重置右键', resp, validator);
sleep(1);
});
}
export function setup() {
console.log('--- setup')
}
export function teardown(data) {
console.log('--- teardown')
}
// 配置ZTF执行时请保留该函数,否则thresholds阀值结果不会影响用例结果
// export function handleSummary(data) {
// return {
// 'results/summary.json': JSON.stringify(data), //the default data object // };
// } function assert (caseId, caseName, checkpoint, data, validator) {
const name = `${caseId} - ${caseName}`
const tags = { id: caseId, name: caseName, checkpoint: checkpoint} check(data, { [name]: validator }, tags);
}
在命令行执行以下命令:
k6 run main.js
得到下列控制台输出:
running (05.0s), 0/3 VUs, 3 complete and 0 interrupted iterations default ✓ [======================================] 0/3 VUs 3s
█ setup
█ 用户登录
✓ 1 - 微信扫码登录
█ 失败次数限制
✓ 0 - 登录失败连续3次,账号锁定
✓ 0 - 登录非连续性失败累计达到3次,账号不锁定
✓ 0 - 锁定账号15分钟后自动解禁
█ CASE
✓ 0 - 邮箱登录
█ 用户管理
✓ 0 - 重置密码
█ teardown
checks.........................: 100.00% ✓ 18 ✗ 0
data_received..................: 20 kB 4.0 kB/s
data_sent......................: 2.2 kB 443 B/s
group_duration.................: avg=857.26ms min=25.43µs med=604.99ms max=2.02s p(90)=1.84s p(95)=1.92s
✓ { group:::登录请求 }.............: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_blocked...............: avg=320.83ms min=0s med=307.26ms max=668.21ms p(90)=655.24ms p(95)=661.73ms
http_req_connecting............: avg=107.18ms min=0s med=101.03ms max=238.79ms p(90)=220.52ms p(95)=229.66ms
http_req_duration..............: avg=392.41ms min=205.38ms med=283.97ms max=702.8ms p(90)=686.37ms p(95)=694.58ms
{ expected_response:true }...: avg=392.41ms min=205.38ms med=283.97ms max=702.8ms p(90)=686.37ms p(95)=694.58ms
✓ { group:::登录请求 }.............: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
✓ { id:1 }.....................: avg=257.77ms min=205.38ms med=209.42ms max=358.51ms p(90)=328.69ms p(95)=343.6ms
http_req_failed................: 0.00% ✓ 0 ✗ 6
http_req_receiving.............: avg=92.33µs min=60µs med=98µs max=125µs p(90)=115.49µs p(95)=120.25µs
http_req_sending...............: avg=348.5µs min=206µs med=306µs max=565µs p(90)=499µs p(95)=531.99µs
http_req_tls_handshaking.......: avg=211.06ms min=0s med=206.07ms max=429.09ms p(90)=427.12ms p(95)=428.1ms
http_req_waiting...............: avg=391.97ms min=204.69ms med=283.6ms max=702.28ms p(90)=685.91ms p(95)=694.09ms
http_reqs......................: 6 1.19129/s
iteration_duration.............: avg=2.05s min=51.71µs med=3.03s max=3.69s p(90)=3.64s p(95)=3.66s
iterations.....................: 3 0.595645/s
vus............................: 1 min=1 max=3
vus_max........................: 3 min=3 max=3
脚本执行结束后,K6会以他默认的形式来展示测试日志和结果。为了达成上述的接口测试目标,下文我们将介绍,如何分析K6测试结果、并提交到禅道测试管理系统中。