发布于 2026-01-06 5 阅读
0

使用 k6 和 Faker 生成数据进行性能测试 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

使用 k6 和 Faker 对生成的数据进行性能测试

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

 

介绍

很多时候,在进行性能测试时,如果测试数据只有细微的差别,可能不会造成太大问题。但在某些情况下,您可能希望尽可能地保持用户交互和数据的真实性。如何在不维护冗长数据表的情况下实现这一点呢?本文将探讨如何利用fakerjs和 k6,使用真实生成的数据执行负载测试。

k6是什么?

k6 是一个开源的性能测试工具,由k6团队编写和维护。该项目的主要目标之一是为用户提供以开发者为中心、代码优先的性能测试方法。

🤓 完全不了解K6?

那么,不妨先从Mostafa Moradian撰写的这篇K6 入门指南开始学习。

什么是Faker?

Faker是一款用于生成逼真数据的工具。它支持多种编程语言,例如PythonRubyPHPJava等等。

在这个特定的例子中,我们将使用 JavaScript 实现 fakerjs,因为它允许我们从测试脚本内部使用它,而不是在执行之前生成数据。

目标

历史上,性能测试很大程度上是通过运行测试,然后手动分析结果来发现性能下降或偏差。k6 采用了一种不同的方法,利用面向目标的性能阈值来创建通过/失败的关卡。让我们为这项测试构建一个场景(或者如果您愿意,也可以称之为用例),并说明它试图衡量什么。

Acme公司情景

Acme公司即将发布一个提交表单,允许用户注册订阅他们的电子报。由于他们计划在黑色星期五期间发布此表单,因此他们希望确保该表单能够承受大量用户同时注册的压力。毕竟,他们是一家业务涵盖所有产品的公司,所以他们预计周五上午的访问量将会激增。

我们的测试目标

虽然我们可以设置复杂的自定义阈值,但通常情况下,基本方法就足够了。在本例中,我们将统计未收到 HTTP OK (200) 状态码的请求数量,以及每个请求的总持续时间。

我们还将使用 300 个虚拟用户进行测试,这些用户将同时执行这些请求。

配置

在k6中,我们将其表示为:




const formFailRate = new Rate('failed form fetches');
const submitFailRate = new Rate('failed form submits');

export const options = {
    // ...
    vus: 300,
    thresholds: {
      'failed form submits': ['rate<0.1'],
      'failed form fetches': ['rate<0.1'],
      'http_req_duration': ['p(95)<400'],
    },
};


Enter fullscreen mode Exit fullscreen mode

这是什么意思?

那么,让我们回顾一下我们在这里所做的工作。假设每秒有 300 个虚拟用户尝试获取并提交订阅表单,我们设定了以下性能目标:

  • 表单检索失败的概率允许低于 10%。
  • 表单数据提交失败的比例允许低于10%。
  • 仅允许 5% 或更少的请求持续时间超过 400 毫秒。

实际测试

现在,我们来看实际的测试代码。测试代码放在一个匿名函数中,每个虚拟用户(VU)在每次迭代中都会执行一次。然后,我们将此函数作为默认导出项公开。

睡眠测试😴

为了确保我们的环境正常运行,我通常先设置一个测试,该测试除了休眠一秒钟之外什么也不做,然后执行一次。



import { sleep } from 'k6';

export default function() {
    sleep(1);
}


Enter fullscreen mode Exit fullscreen mode

运行后,会产生类似这样的输出:

运行一个只包含 sleep 的 k6 脚本

添加我们的阈值



import { sleep } from 'k6';
import { Rate } from 'k6/metrics';

const formFailRate = new Rate('failed form fetches');
const submitFailRate = new Rate('failed form submits');

export const options = {
  // ...
  vus: 300,
  duration: '10s',
  thresholds: {
    'failed form submits': ['rate<0.1'],
    'failed form fetches': ['rate<0.1'],
    'http_req_duration': ['p(95)<400'],
  },
};

export default function() {
  formFailRate.add(0);
  submitFailRate.add(0);
  sleep(1);
}


Enter fullscreen mode Exit fullscreen mode

注意默认函数中的两行新代码吗?现在,每次迭代我们都会向阈值指标添加数据点,表明请求没有失败。接下来,我们会将这些数据点连接起来,执行一些有意义的操作。此外,我们还添加了持续时间,使脚本能够运行多次迭代。

目前,运行该脚本应该会输出以下结果:

运行带有睡眠和速率的 k6 脚本

耶,通过了!两个绿色对勾!

添加请求

为了能够测量出有用的数据,我们还需要添加一些实际的请求。在这个例子中,我们将使用https://httpbin.test.loadimpact.com/作为我们的 API,它是我们镜像的热门工具HTTPBin。您可以随意使用任何您喜欢的 HTTP 请求接收器!



import { sleep } from 'k6';
import { Rate } from 'k6/metrics';
import http from 'k6/http';

const baseUrl = 'https://httpbin.test.loadimpact.com/anything';
const urls = {
  form: `${baseUrl}/form`,
  submit: `${baseUrl}/form/subscribe`,
};


const formFailRate = new Rate('failed form fetches');
const submitFailRate = new Rate('failed form submits');

export const options = {
    vus: 300,
    duration: '10s',
    thresholds: {
      'failed form submits': ['rate<0.1'],
      'failed form fetches': ['rate<0.1'],
      'http_req_duration': ['p(95)<400'],
    },
};

const getForm = () => {
  const formResult = http.get(urls.form);
  formFailRate.add(formResult.status !== 200);
}

const submitForm = () => {
  const submitResult = http.post(urls.submit, {});
  submitFailRate.add(submitResult.status !== 200);
}

export default function() {
  getForm();
  submitForm();
  sleep(1);
}


Enter fullscreen mode Exit fullscreen mode

再说一遍:

使用请求运行 k6

现在输出结果还包括有关我们 HTTP 请求的指标,以及持续时间旁边的绿色小勾。

添加捆绑和转译

现在脚本已经可以运行了,差不多可以添加 Faker 了。在此之前,我们需要确保 k6 可以使用 Faker 库。

由于 k6 并非运行在 NodeJS 环境中,而是运行在 goja 虚拟机中,因此需要一些辅助工具。好在,这并不复杂。我们将使用 webpack 和 babel 来实现这一点,但任何与 babel 兼容的打包工具应该都可以。

我们先来初始化一个 npm 包,并添加所有需要的依赖项:



$ yarn init -y && yarn add \
    @babel/core \
    @babel/preset-env \
    babel-loader \
    core-js \
    webpack \
    webpack-cli


Enter fullscreen mode Exit fullscreen mode

接下来,我们将创建 webpack 配置。webpack 和 babel 的具体细节不在本文讨论范围内,但网上有很多优秀的资源介绍它们的工作原理。



// webpack.config.js

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'test.[name].js',
        libraryTarget: 'commonjs'
    },
    module: {
        rules: [
            { test: /\.js$/, use: 'babel-loader' },
        ]
    },
    stats: {
        colors: true
    },
    target: "web",
    externals: /k6(\/.*)?/,
    devtool: 'source-map',
}


Enter fullscreen mode Exit fullscreen mode

以及该.babelrc文件:



{
    "presets": [
      [
        "@babel/preset-env",
        {
          "useBuiltIns": "usage",
          "corejs": 3
        }
      ]
    ]
  }


Enter fullscreen mode Exit fullscreen mode

我们还会修改 package.json 文件,以便可以使用 yarn 启动测试:



{
  "name": "k6-faker",
  "scripts": {
+   "pretest": "webpack",
+   "test": "k6 run ./dist/test.main.js"
  },
  ...
}


Enter fullscreen mode Exit fullscreen mode

🧠 你知道吗?

在脚本名称开头使用pre`--or` ,会导致该脚本在你要调用的脚本之前/之后运行。在本例中,该脚本确保每次运行测试时,webpack 都会首先从源代码创建一个新的、全新的 bundle。——很棒吧?👍🏻postpretest


登场 Faker!

那我们就直接开始吧!第一步是将 faker 添加到我们的依赖项中:



$ yarn add faker


Enter fullscreen mode Exit fullscreen mode

Faker 拥有一个相当庞大的数据库,可以生成从公司详情到宣传语和头像等各种数据。虽然这些数据都很有用,但我们只会用到 Faker 提供的其中一小部分。我们的对象结构如下:



{
  name: 'jane doe',
  title: 'intergalactic empress',
  company: 'Worldeaters Inc',
  email: 'jane@doe.example',
  country: 'N/A'
}


Enter fullscreen mode Exit fullscreen mode

接下来,我们将创建一个服务,该服务可用于生成上述人员信息:



// subscriber.js

import * as faker from 'faker/locale/en_US'; 

export const generateSubscriber = () => ({
    name: `SUBSCRIPTION_TEST - ${faker.name.firstName()} ${faker.name.lastName()}`,
    title: faker.name.jobTitle(),
    company: faker.company.companyName(),
    email: faker.internet.email(),
    country: faker.address.country()
});


Enter fullscreen mode Exit fullscreen mode

👿 前方可能出现性能问题!

添加的所有依赖项都会在一定程度上增加内存消耗,尤其是在并发实例扩展到 300 个时。因此,至关重要的是,我们只导入测试用例中使用的语言环境。

在为本文准备示例存储库时,我注意到使用 faker 会为每个 VU 增加约 2.3MB 的内存,300 个 VU 的总内存占用量约为 1.5GB。

您可以点击此处阅读更多关于k6中javascript性能以及如何调整它的信息

您可能已经注意到,我们在生成的用户名前添加了前缀SUBSCRIPTION_TEST。为测试数据添加唯一标识符只是为了方便快速筛选出所有我在测试过程中创建的虚拟数据。虽然这不是必须的,但通常来说,这是一个好主意——尤其是在您无法轻松清理的测试环境中进行测试时。


最终组装

现在,让我们把所有内容整合起来!



// index.js

import { sleep } from 'k6';
import http from 'k6/http';
import { Rate } from 'k6/metrics';

import { generateSubscriber } from './subscriber';

const baseUrl = 'https://httpbin.test.loadimpact.com/anything';
const urls = {
  form: `${baseUrl}/form`,
  submit: `${baseUrl}/form/subscribe`,
};

const formFailRate = new Rate('failed form fetches');
const submitFailRate = new Rate('failed form submits');

export const options = {
  vus: 300,
  duration: '10s',
  thresholds: {
    'failed form submits': ['rate<0.1'],
    'failed form fetches': ['rate<0.1'],
    'http_req_duration': ['p(95)<400']
  }
};

const getForm = () => {
    const formResult = http.get(urls.form);
    formFailRate.add(formResult.status !== 200);
}

const submitForm = () => {
    const person = generateSubscriber();    
    const payload = JSON.stringify(person);

    const submitResult = http.post(urls.submit, payload);
    submitFailRate.add(submitResult.status !== 200);
}

export default function() {
    getForm();
    submitForm();
    sleep(1);
}


Enter fullscreen mode Exit fullscreen mode


// subscriber.js

import * as faker from 'faker/locale/en_US'; 

export const generateSubscriber = () => ({
    name: `SUBSCRIPTION_TEST - ${faker.name.firstName()} ${faker.name.lastName()}`,
    title: faker.name.jobTitle(),
    company: faker.company.companyName(),
    email: faker.internet.email(),
    country: faker.address.country()
});


Enter fullscreen mode Exit fullscreen mode

就这样,我们准备出发了:

使用 faker、阈值和 HTTP 请求运行 k6


结语

虽然将 k6 中使用的 JavaScript 引擎与 webpack 和 babel 结合使用可以带来近乎无限的灵活性,但密切关注实际测试的内存消耗和性能至关重要。毕竟,由于负载生成器资源不足而导致的误报毫无益处。

本文中的所有代码都可以在
GitHub上的示例存储库中找到,我会尽量使其与 k6 和 faker 的新版本保持同步。

我很想听听大家的想法,所以请在下方留言区留下您的问题和评论。👇🏼

文章来源:https://dev.to/k6/performance-testing-with- generated-data-using-k6-and-faker-2e