12

As indicated in the official loadimpact/k6 documentation, we are able to execute a single k6 script as follows:

k6 run ../tests/http_get.js

How would I go about executing multiple script files in a single run? Specifically all scripts that reside in a given local directory. Something like:

k6 run ../tests/

Is this supported out of the box by k6?

101010110101
  • 1,940
  • 8
  • 31
  • 42

5 Answers5

12

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

You could easily write some more complicated bash scripting to read in everything from the /tests/ directory and run them like that. I chose to do it like this though because I had some custom input params to give to each specific test.

Another way would be to write a docker compose script to do pretty much the same thing. This would start up a docker container for each test and run it inside there. The k6 docker image is nothing more than a tiny linux image with the k6 binary added to it.

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"

Both of these methods should allow you to run multiple tests at the same time in a CI environment as well as on your local machine.

zypherman
  • 647
  • 5
  • 15
  • 1
    these are both great suggestions, I particularly like the docker compose approach. Thanks! – 101010110101 Apr 10 '18 at 23:18
  • If you need some more help on things feel free to reach out to me directly. I have implemented this particular framework at 2 different companies so I have learned a lot of its in's and outs. – zypherman Apr 11 '18 at 00:35
  • @zypherman : What is the appropriate way to run multiple scenarios if we have more than 500 scenarios to test ? I am using executor: 'per-vu-iterations' as I need to run 1 request per scenario with 1 iteration. – Kishan Patel Jul 07 '21 at 19:15
4

At the moment, k6 only accepts one script file, and it runs the exported default function.

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

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

Perhaps, you can accomplish your goal by using modules. Splitting your logic into modules helps to organize your code and allows reusing your common use cases in different tests.

Check out the k6 Modules documentation

import {sleep} from "k6";
import mainPageUserFlow from "../cases/main-page";
import billingUserFlow from "../cases/billing";

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

Additionally, you could also change the execution of the different Virtual Users on your script like https://community.k6.io/t/how-to-distribute-vus-across-different-scenarios-with-k6/49

ppcano
  • 2,831
  • 1
  • 24
  • 19
  • This is what we're doing. Our issue is the thresholds we specified in our config are shared for all the modules. Which is not ideal... – Storm Muller Mar 11 '19 at 14:16
  • 1
    @StormMuller If you want to set a threshold on the http_duration metric, you could use a `group` and the `group_duration` metric like https://gist.github.com/ppcano/1442dc243321747ee146a6c90859fe89 . I recommend you post your example at https://community.k6.io/ to assist you better. – ppcano Mar 12 '19 at 10:23
  • 1
    In the gist, I created a threshold on a system metric `group_duration` with a system tag `group` https://docs.k6.io/docs/tags-and-groups#section-system-tags. Tags and custom metrics are very flexible and powerful; you could define a threshold on any custom metric with any custom tag. – ppcano Mar 12 '19 at 10:39
0

Using & will run the tests in parallel, if you want to run sequentially and retrieve the combined result, I suggest:

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

You can set different scenarios and point to different files containing the script to run each one, as per the docs here https://k6.io/docs/using-k6/k6-options/reference/#scenarios

Here is an example:

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 }),
  };
}
LeaveTheCapital
  • 2,530
  • 1
  • 7
  • 11
0

When I was doing first steps in k6, I wanted to see one example with values.
So here is the sample, where my 'http://localhost:3000/#/' = OWASP Juice Shop (free web page for security testing training. Page that you can install and run locally):

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,
  });
}

P.S. Solution tested with k6 v0.43.1 (2023.04.20)