数据参数化
数据参数化是将测试值转换为可重用参数的过程,例如通过变量和共享数组。
本页面提供了一些如何在测试脚本中参数化数据的示例。当虚拟用户 (VU) 在测试中发起 POST、PUT 或 PATCH 请求时,通常需要参数化。当您需要从单独的文件添加测试数据时,也可以使用参数化。
参数化有助于防止服务器端缓存影响您的负载测试。反过来,这将使您的测试更加真实。
SharedArray 的性能影响
k6 中的每个 VU 都是一个独立的 JS VM。为了防止整个数据文件被多次复制,添加了 SharedArray。与普通的非共享数组相比,访问元素会产生一些 CPU 开销,但与发出请求所需的时间相比,这种差异可以忽略不计。与大型文件不使用它相比,这更不是问题,因为否则 k6 将使用过多的内存来运行,这可能导致您的脚本根本无法运行,或者在系统资源耗尽时中止。
例如,Cloud 服务为每 300 个 VU 分配 8GB 内存。因此,如果您的文件足够大并且您没有使用 SharedArray,则可能意味着您的脚本会在某个时候耗尽内存。此外,即使内存足够,k6 也有垃圾收集器(因为它是由 golang 编写的),它会遍历所有可访问的对象(包括 JS 对象),并找出哪些需要进行垃圾收集。对于复制数百次的巨型 JS 数组,这会增加相当多的额外工作。
关于 SharedArray
性能特征的说明可以在其 API 文档中找到。
从 JSON 文件
{
"users": [
{ "username": "test", "password": "qwerty" },
{ "username": "test", "password": "qwerty" }
]
}
import { SharedArray } from 'k6/data';
// not using SharedArray here will mean that the code in the function call (that is what loads and
// parses the json) will be executed per each VU which also means that there will be a complete copy
// per each VU
const data = new SharedArray('some data name', function () {
return JSON.parse(open('./data.json')).users;
});
export default function () {
const user = data[0];
console.log(data[0].username);
}
从 CSV 文件
k6 本身不支持解析 CSV 文件,但您可以使用外部库 Papa Parse。
您可以下载该库并像这样本地导入
import papaparse from './papaparse.js';
import { SharedArray } from 'k6/data';
// not using SharedArray here will mean that the code in the function call (that is what loads and
// parses the csv) will be executed per each VU which also means that there will be a complete copy
// per each VU
const csvData = new SharedArray('another data name', function () {
// Load CSV file and parse it using Papa Parse
return papaparse.parse(open('./data.csv'), { header: true }).data;
});
export default function () {
// ...
}
或者您可以直接从 jslib.k6.io 获取,像这样。
import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js';
import { SharedArray } from 'k6/data';
// not using SharedArray here will mean that the code in the function call (that is what loads and
// parses the csv) will be executed per each VU which also means that there will be a complete copy
// per each VU
const csvData = new SharedArray('another data name', function () {
// Load CSV file and parse it using Papa Parse
return papaparse.parse(open('./data.csv'), { header: true }).data;
});
export default function () {
// ...
}
下面是使用 Papa Parse 解析包含用户名/密码对的 CSV 文件并使用该数据登录 test.k6.io 测试站点的示例
/* Where contents of data.csv is:
username,password
admin,123
test_user,1234
*/
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js';
// not using SharedArray here will mean that the code in the function call (that is what loads and
// parses the csv) will be executed per each VU which also means that there will be a complete copy
// per each VU
const csvData = new SharedArray('another data name', function () {
// Load CSV file and parse it using Papa Parse
return papaparse.parse(open('./data.csv'), { header: true }).data;
});
export default function () {
// Now you can use the CSV data in your test logic below.
// Below are some examples of how you can access the CSV data.
// Loop through all username/password pairs
for (const userPwdPair of csvData) {
console.log(JSON.stringify(userPwdPair));
}
// Pick a random username/password pair
const randomUser = csvData[Math.floor(Math.random() * csvData.length)];
console.log('Random user: ', JSON.stringify(randomUser));
const params = {
login: randomUser.username,
password: randomUser.password,
};
console.log('Random user: ', JSON.stringify(params));
const res = http.post('https://test.k6.io/login.php', params);
check(res, {
'login succeeded': (r) => r.status === 200 && r.body.indexOf('successfully authorized') !== -1,
});
sleep(1);
}
获取唯一数据
在测试中通常要求数据不能重复使用。借助 k6/execution,它包含一个 scenario.iterationInTest
属性,您可以从数据集中获取唯一的行。
⚠️ 多个场景
scenario.iterationInTest
属性在每个场景中是唯一的,而不是整个测试中。这意味着如果您的测试中有多个场景,您可能需要按场景分割数据。
import { SharedArray } from 'k6/data';
import { scenario } from 'k6/execution';
const data = new SharedArray('users', function () {
return JSON.parse(open('./data.json')).users;
});
export const options = {
scenarios: {
'use-all-the-data': {
executor: 'shared-iterations',
vus: 10,
iterations: data.length,
maxDuration: '1h',
},
},
};
export default function () {
// this is unique even in the cloud
const user = data[scenario.iterationInTest];
console.log(`user: ${JSON.stringify(user)}`);
}
另外,如果您的用例要求每个 VU 使用一个唯一的数据集,您可以利用一个名为 vu.idInTest
的属性。
在下面的示例中,我们将使用 per-vu-iterations
执行器来确保每个 VU 完成固定数量的迭代。
import { sleep } from 'k6';
import { SharedArray } from 'k6/data';
import { vu } from 'k6/execution';
const users = new SharedArray('users', function () {
return JSON.parse(open('./data.json')).users;
});
export const options = {
scenarios: {
login: {
executor: 'per-vu-iterations',
vus: users.length,
iterations: 20,
maxDuration: '1h30m',
},
},
};
export default function () {
// VU identifiers are one-based and arrays are zero-based, thus we need - 1
console.log(`Users name: ${users[vu.idInTest - 1].username}`);
sleep(1);
}
使用 faker.js 生成数据
以下文章展示了如何在 k6 中使用 faker.js 在测试执行期间生成真实数据