重用和重新运行测试
在之前的教程中,您设计了 k6 脚本来断言性能并方便比较结果。
在本教程中,学习如何
- 将测试脚本模块化为可重用组件
- 使用环境变量动态配置脚本
示例脚本
为了好玩,让我们结合之前的教程中的脚本。使用 multiple-flows.js
测试的逻辑与 api-test.js
的阈值和场景(随意添加更多检查、请求、组等)。请注意这个脚本的特点:
default
函数有两个组:Contacts flow
和Coinflip game
options
对象有两个属性:thresholds
和scenarios
在以下部分,学习如何将这些组件拆分到单独的文件中,并在运行时动态组合它们。
import http from 'k6/http';
import { group, sleep } from 'k6';
import { Trend } from 'k6/metrics';
//define configuration
export const options = {
scenarios: {
//arbitrary name of scenario:
breaking: {
executor: 'ramping-vus',
stages: [
{ duration: '10s', target: 20 },
{ duration: '50s', target: 20 },
{ duration: '50s', target: 40 },
{ duration: '50s', target: 60 },
{ duration: '50s', target: 80 },
{ duration: '50s', target: 100 },
{ duration: '50s', target: 120 },
{ duration: '50s', target: 140 },
//....
],
},
},
//define thresholds
thresholds: {
http_req_failed: [{ threshold: 'rate<0.01', abortOnFail: true }], // availability threshold for error rate
http_req_duration: ['p(99)<1000'], // Latency threshold for percentile
},
};
//set baseURL
const baseUrl = 'https://test.k6.io';
// Create custom trends
const contactsLatency = new Trend('contacts_duration');
const coinflipLatency = new Trend('coinflip_duration');
export default function () {
// Put visits to contact page in one group
let res;
group('Contacts flow', function () {
// save response as variable
res = http.get(`${baseUrl}/contacts.php`);
// add duration property to metric
contactsLatency.add(res.timings.duration);
sleep(1);
res = http.get(`${baseUrl}/`);
// add duration property to metric
contactsLatency.add(res.timings.duration);
sleep(1);
});
// Coinflip players in another group
group('Coinflip game', function () {
// save response as variable
let res = http.get(`${baseUrl}/flip_coin.php?bet=heads`);
// add duration property to metric
coinflipLatency.add(res.timings.duration);
sleep(1);
res = http.get(`${baseUrl}/flip_coin.php?bet=tails`);
// add duration property to metric
coinflipLatency.add(res.timings.duration);
sleep(1);
});
}
模块化逻辑
使用模块,您可以从其他文件中使用逻辑和变量。使用模块将函数提取到它们自己的文件中。
为此,请按照以下步骤操作:
复制前面的脚本(
whole-tutorial.js
)并将其另存为main.js
。从
main.js
脚本文件中提取Contacts flow
组函数,并将其粘贴到名为contacts.js
的新文件中。export function contacts() { group('Contacts flow', function () { // save response as variable let res = http.get(`${baseUrl}/contacts.php`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); res = http.get(`${baseUrl}/`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); }); }
这样,这个脚本将无法工作,因为它有未声明的函数和变量。
添加必要的导入和变量。这个脚本使用了
group
,sleep
, 和http
函数或库。它还有一个自定义指标。由于此指标特定于该组,因此您可以将其添加到contacts.js
中。最后,将
baseUrl
作为contacts
函数的参数传递。import http from 'k6/http'; import { Trend } from 'k6/metrics'; import { group, sleep } from 'k6'; const contactsLatency = new Trend('contact_duration'); export function contacts(baseUrl) { group('Contacts flow', function () { // save response as variable let res = http.get(`${baseUrl}/contacts.php`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); res = http.get(`${baseUrl}/`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); }); }
在名为
coinflip.js
的文件中对coinflip
组重复此过程。使用标签页查看最后三个文件(options
已移到main.js
的底部,以便更好地阅读)。import { contacts } from './contacts.js'; import { coinflip } from './coinflip.js'; const baseUrl = 'https://test.k6.io'; export default function () { // Put visits to contact page in one group contacts(baseUrl); // Coinflip players in another group coinflip(baseUrl); } //define configuration export const options = { scenarios: { //arbitrary name of scenario: breaking: { executor: 'ramping-vus', stages: [ { duration: '10s', target: 20 }, { duration: '50s', target: 20 }, { duration: '50s', target: 40 }, { duration: '50s', target: 60 }, { duration: '50s', target: 80 }, { duration: '50s', target: 100 }, { duration: '50s', target: 120 }, { duration: '50s', target: 140 }, //.... ], }, }, //define thresholds thresholds: { http_req_failed: [{ threshold: 'rate<0.01', abortOnFail: true }], // availability threshold for error rate http_req_duration: ['p(99)<1000'], // Latency threshold for percentile }, };
import http from 'k6/http'; import { Trend } from 'k6/metrics'; import { group, sleep } from 'k6'; const contactsLatency = new Trend('contact_duration'); export function contacts(baseUrl) { group('Contacts flow', function () { // save response as variable let res = http.get(`${baseUrl}/contacts.php`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); res = http.get(`${baseUrl}/`); // add duration property to metric contactsLatency.add(res.timings.duration); sleep(1); }); }
import http from 'k6/http'; import { Trend } from 'k6/metrics'; import { group, sleep } from 'k6'; const coinflipLatency = new Trend('coinflip_duration'); export function coinflip(baseUrl) { group('Coinflip game', function () { // save response as variable let res = http.get(`${baseUrl}/flip_coin.php?bet=heads`); // add duration property to metric coinflipLatency.add(res.timings.duration); sleep(1); // mutate for new request res = http.get(`${baseUrl}/flip_coin.php?bet=tails`); // add duration property to metric coinflipLatency.add(res.timings.duration); sleep(1); }); }
运行测试
# setting the workload to 10 iterations to limit run time k6 run main.js --iterations 10
结果应与在组合文件中运行脚本非常相似,因为它们是同一个测试。
模块化工作负载
现在迭代代码完全模块化了,您也可以将 options
模块化。
以下示例创建了一个模块 config.js
来导出阈值和工作负载设置。
import { coinflip } from './coinflip.js';
import { contacts } from './contacts.js';
import { thresholdsSettings, breakingWorkload } from './config.js';
export const options = {
scenarios: { breaking: breakingWorkload },
thresholds: thresholdsSettings,
};
const baseUrl = 'https://test.k6.io';
export default function () {
contacts(baseUrl);
coinflip(baseUrl);
}
export const thresholdsSettings = {
http_req_failed: [{ threshold: 'rate<0.01', abortOnFail: true }],
http_req_duration: ['p(99)<1000'],
};
export const breakingWorkload = {
executor: 'ramping-vus',
stages: [
{ duration: '10s', target: 20 },
{ duration: '50s', target: 20 },
{ duration: '50s', target: 40 },
{ duration: '50s', target: 60 },
{ duration: '50s', target: 80 },
{ duration: '50s', target: 100 },
{ duration: '50s', target: 120 },
{ duration: '50s', target: 140 },
//....
],
};
注意这个最终脚本的长度,并与本页开头的脚本进行比较。虽然最终执行相同,但它的大小减半,并且更易读。
除了简洁性之外,这种模块化还允许您从多个部分组合脚本,或在运行时动态配置脚本。
混合搭配逻辑
通过模块化的配置和逻辑,您可以混合搭配逻辑。一种简单的配置方法是通过环境变量。
修改 main.js
和 config.js
,使其
- 默认情况下运行一个 5 次迭代的冒烟测试
- 通过正确的环境变量值,运行一个破坏性测试
为此,请按照以下步骤操作:
将配置冒烟测试的工作负载设置添加到
config.js
中。export const smokeWorkload = { executor: 'shared-iterations', iterations: 5, vus: 1, }; export const thresholdsSettings = { http_req_failed: [{ threshold: 'rate<0.01', abortOnFail: true }], http_req_duration: ['p(99)<1000'], }; export const breakingWorkload = { executor: 'ramping-vus', stages: [ { duration: '10s', target: 20 }, { duration: '50s', target: 20 }, { duration: '50s', target: 40 }, { duration: '50s', target: 60 }, { duration: '50s', target: 80 }, { duration: '50s', target: 100 }, { duration: '50s', target: 120 }, { duration: '50s', target: 140 }, //.... ], };
编辑
main.js
以根据WORKLOAD
环境变量选择工作负载设置。例如:import { coinflip } from './coinflip.js'; import { contacts } from './contacts.js'; import { thresholdsSettings, breakingWorkload, smokeWorkload } from './config.js'; export const options = { scenarios: { my_scenario: __ENV.WORKLOAD === 'breaking' ? breakingWorkload : smokeWorkload, }, thresholds: thresholdsSettings, }; const baseUrl = 'https://test.k6.io'; export default function () { contacts(baseUrl); coinflip(baseUrl); }
运行脚本时带或不带
-e
标志。- 当您运行
k6 run main.js
时会发生什么? - 那么运行
k6 run main.js -e WORKLOAD=breaking
呢?
- 当您运行
这是一个简单的例子,展示了如何模块化测试。随着您的测试套件增长以及更多人参与性能测试,您的模块化策略对于构建和维护高效测试套件至关重要。
下一步
现在您已经看到了编写测试、断言性能、过滤结果和模块化脚本的示例。请注意测试如何在复杂性上循序渐进:从单个端点到整体测试,从小型负载到大型负载,以及从单个测试到可重用模块。这些进展在测试中是典型的,下一步是自动化。自动化本教程可能不切实际,但如果您感兴趣,请阅读自动化性能测试指南。
更可能的是,您想了解更多关于 k6 的信息。k6-learn repository 中有更多详细的练习。或者,您可以阅读和探索测试指南,并尝试构建您的测试策略。
祝您测试愉快!