如何通过单个命令在loadimpact/k6中执行多个k6脚本?

12
根据官方的loadimpact/k6文档,我们可以按如下方式执行单个k6脚本:

如上所述

k6 run ../tests/http_get.js

我该如何在单次运行中执行多个脚本文件?特别是指位于给定本地目录中的所有脚本。例如:

k6 run ../tests/

k6原生支持这个吗?

5个回答

12

根据您的设置,有几种不同的方法可以解决这个问题。一个相当直接的方法是在 Bash 中分叉 k6 运行命令。

Depending on your setup there are a couple different ways you can solve this. A pretty straight forward way is to fork the k6 run command inside bash.

#!/bin/sh 
k6 run test1_spec.js &
k6 run test2_spec.js &
k6 run test3_spec.js

你可以轻松编写一些更复杂的Bash脚本来读取/tests/目录中的所有内容并运行它们。但我选择这样做是因为我需要为每个特定的测试提供一些自定义输入参数。

另一种方法是编写一个Docker Compose脚本来完成几乎相同的操作。这将为每个测试启动一个Docker容器并在其中运行。 k6 Docker镜像只不过是添加了k6二进制文件的小型Linux镜像而已。

version: '3'
services:
  k6_test:
    image: loadimpact/k6
    container_name: test_k6
    volumes:
       - ./:/specs
    command: run /tests/test_spec.js
    ports:
       - "6565:6565"

  k6_test2:
    image: loadimpact/k6
    container_name: test2_k6
    volumes:
       - ./:/specs
    command: run /tests/test2_spec.js
    ports:
       - "6566:6566"

这两种方法都应该允许你在CI环境和本地机器上同时运行多个测试。


1
这些都是很好的建议,我特别喜欢Docker Compose的方法。谢谢! - 101010110101
如果你需要更多的帮助,请直接联系我。我已经在两家不同的公司实施了这个特定的框架,因此我学到了很多它的细节。 - zypherman
@zypherman:如果我们有超过500个场景需要测试,运行多个场景的适当方式是什么?我正在使用executor: 'per-vu-iterations',因为我需要每个场景每次迭代运行1个请求。 - Kishan Patel

4

目前,k6只接受一个脚本文件,并运行默认导出的函数。

import {sleep} from "k6";
import http from "k6/http";

export default function() {
    http.get("http://test.loadimpact.com/");
    sleep(2);
}

也许,您可以通过使用模块来实现您的目标。 将逻辑拆分为模块有助于组织代码,并允许在不同的测试中重复使用常见用例。
请查看k6模块文档
import {sleep} from "k6";
import mainPageUserFlow from "../cases/main-page";
import billingUserFlow from "../cases/billing";

export default function() {
    mainPageUserFlow();
    billingUserFlow();
    sleep(2);
}

此外,您还可以更改脚本中不同虚拟用户的执行方式,就像https://community.k6.io/t/how-to-distribute-vus-across-different-scenarios-with-k6/49所示。请注意保留HTML标记。

这就是我们正在做的事情。我们的问题是我们在配置文件中指定的阈值适用于所有模块。这不是理想的情况... - Storm Muller
1
@StormMuller 如果你想在 http_duration 指标上设置阈值,你可以使用 groupgroup_duration 指标,就像这个链接中的示例 https://gist.github.com/ppcano/1442dc243321747ee146a6c90859fe89 一样。我建议你将你的示例发布在 https://community.k6.io/ 上,以便更好地帮助你。 - ppcano
1
在这个要点中,我使用系统标签group和系统度量group_duration创建了一个阈值。https://docs.k6.io/docs/tags-and-groups#section-system-tags。标签和自定义度量非常灵活和强大;您可以在任何自定义标记的任何自定义度量上定义阈值。 - ppcano

1

您可以设置不同的场景并指向包含脚本的不同文件以运行每个场景,如此文档所述 https://k6.io/docs/using-k6/k6-options/reference/#scenarios

以下是一个示例:

import { default as firstScenario } from './firstScenario';
import { default as secondScenario } from './secondScenario';

export const options: Options = {
  thresholds: {
    http_req_duration: [`p(99)<${httpReqDuration}`],
    checks: ['rate>0.80'],
  },
  scenarios: {
    scriptAuthenticatedScenario: {
      exec: 'myFirstScenario',
      executor: 'constant-vus',
      vus,
      duration,
    },
    scriptUnauthenticatedScenario: {
      exec: 'mySecondScenario',
      executor: 'constant-vus',
      vus,
      duration,
    },
  },
};

export function myFirstScenario() {
  firstScenario();
}

export function mySecondScenario() {
  secondScenario();
}

export function handleSummary(data) {
  return {
    'results/scenarios.html': htmlReport(data),
    stdout: textSummary(data, { indent: ' ', enableColors: true }),
  };
}


0

使用 & 将会并行运行测试,如果你想要按顺序运行并获取合并结果,我建议:

exit_c=0 
(
  k6 run script_1.js || exit_c=$?
  k6 run script_2.js || exit_c=$?
  ...
  k6 run script_n.js || exit_c=$?
  exit $exit_c
)

0

当我刚开始学习 k6 时,我想看一个带有值的例子。
因此,这是一个示例,在我的'http://localhost: 3000/#/'下 = OWASP Juice Shop(用于安全测试培训的免费网页。您可以在本地安装和运行的页面):

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

export const requests = new Counter('http_reqs');
const myFailRate = new Rate('failed requests');

export const options = {
  scenarios: {
    main: {  //Test Case 1
      executor: 'constant-vus',
      exec: 'main',
      vus: 30,
      duration: '90s',
    },
    login: {   //Test Case 2
      executor: 'constant-vus',
      exec: 'login',
      vus: 10,
      startTime: '15s',
      duration: '90s',
    },
    about: {   //Test Case 3
      executor: 'shared-iterations',
      exec: 'about',
      vus: 5,
      iterations: 50,
      startTime: '30s',
      maxDuration: '90s',      
    },
  }
}

export function main() { //Test Case 1
  const res = http.get('http://localhost:3000/#/');
  sleep(Math.random() * 5);
  myFailRate.add(res.status !== 200);
  const checkRes = check(res, {
    'status was 200': (r) => r.status == 200,
    'response body contains <OWASP Juice Shop>': (r) => r.body.indexOf('OWASP Juice Shop') !== -1,
    'duration was <=200ms(miliseconds)': (r) => r.timings.duration <= 200,
  });
}

export function login() { //Test Case 2
  const res = http.get('http://localhost:3000/#/login');
  sleep(Math.random() * 2);  
  myFailRate.add(res.status !== 200);
  const checkRes = check(res, {
    'status was 200': (r) => r.status == 200,
    'response body contains <Login1>': (r) => r.body.includes("Login"),  //Failure expected here. Not found, Yet I see it on the page
    'response body contains <Login2>': (r) => r.body.includes('Login'),  //Failure expected here. Not found, Yet I see it on the page
    'response body contains <Login3>': (r) => r.body.indexOf('Login') !== -1,  //Failure expected here. Not found, Yet I see it on the page
    'response body contains <Login4>': (r) => r.body.indexOf("Login") !== -1,  //Failure expected here. Not found, Yet I see it on the page
    'response body contains <OWASP Juice Shop> in the Login page': (r) => r.body.indexOf('OWASP Juice Shop') !== -1,
    'duration was <=200ms(Login)': (r) => r.timings.duration <= 200,
  });
}

export function about() { //Test Case 3
  const res = http.get('http://localhost:3000/#/about')
  sleep(Math.random() * 5);
  myFailRate.add(res.status !== 200);
  const checkRes = check(res, {
    'status was 200': (r) => r.status == 200,
    'response body contains <About Us>': (r) => r.body.includes('About Us'), //Failure expected here. Not found, Yet I see it on the page
    'response body contains <OWASP Juice Shop> in the About page': (r) => r.body.indexOf('OWASP Juice Shop') !== -1,
    'duration was <=200ms(About)': (r) => r.timings.duration <= 200,
  });
}

附注:解决方案已在k6 v0.43.1 (2023.04.20)上进行测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接