菜单
文档breadcrumb arrow Grafana k6breadcrumb arrow 示例breadcrumb arrow 数据参数化
开源

数据参数化

数据参数化是将测试值转换为可重用参数的过程,例如通过变量和共享数组。

本页面提供了一些如何在测试脚本中参数化数据的示例。当虚拟用户 (VU) 在测试中发起 POST、PUT 或 PATCH 请求时,通常需要参数化。当您需要从单独的文件添加测试数据时,也可以使用参数化。

参数化有助于防止服务器端缓存影响您的负载测试。反过来,这将使您的测试更加真实。

SharedArray 的性能影响

k6 中的每个 VU 都是一个独立的 JS VM。为了防止整个数据文件被多次复制,添加了 SharedArray。与普通的非共享数组相比,访问元素会产生一些 CPU 开销,但与发出请求所需的时间相比,这种差异可以忽略不计。与大型文件不使用它相比,这更不是问题,因为否则 k6 将使用过多的内存来运行,这可能导致您的脚本根本无法运行,或者在系统资源耗尽时中止。

例如,Cloud 服务为每 300 个 VU 分配 8GB 内存。因此,如果您的文件足够大并且您没有使用 SharedArray,则可能意味着您的脚本会在某个时候耗尽内存。此外,即使内存足够,k6 也有垃圾收集器(因为它是由 golang 编写的),它会遍历所有可访问的对象(包括 JS 对象),并找出哪些需要进行垃圾收集。对于复制数百次的巨型 JS 数组,这会增加相当多的额外工作。

关于 SharedArray 性能特征的说明可以在其 API 文档中找到。

从 JSON 文件

json
{
  "users": [
    { "username": "test", "password": "qwerty" },
    { "username": "test", "password": "qwerty" }
  ]
}
JavaScript
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

您可以下载该库并像这样本地导入

JavaScript
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 获取,像这样。

JavaScript
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 测试站点的示例

JavaScript
/*  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 属性在每个场景中是唯一的,而不是整个测试中。这意味着如果您的测试中有多个场景,您可能需要按场景分割数据。

JavaScript
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 完成固定数量的迭代。

JavaScript
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 在测试执行期间生成真实数据