How would I go about executing multiple script files in a single run?

Development team has provided Postman collection for performance testing. There would be around 20 collection. Each collection represents end to end micro services. I am using Postman to K6 converter. It gives me 20 JS files. VU level , stages/options and threshold/ checks needs to be different for each K6 JS file. This project would be added inside Git. We need to define pipeline to execute all the JS files.

APPROACH 1

I decide to write let’s say start.js and then call all other 20 JS files within start.js. I will invoke start.js from Git yml file.
Sample start.js looks as below-

import "./PostmanScripts/libs/shim/core.js";
import "./PostmanScripts/libs/shim/urijs.js";

import { group, http, sleep } from 'k6';
import startSiteTest from "./PostmanScripts/Test/Site/startSiteTest.js";

export let options = {
  stages: testData.siteCRUDOptions,
  teardownTimeout: testData.teardownTimeout,
  thresholds: testData.thresholds
}

postman[Symbol.for("initial")]({
  options,
  collection: {
  },
  environment: {
    AUTH0_BASE_URL: `${__ENV.AUTH0_BASE_URL}`,
  }
});

export default function () {
  //Call Test Collection 1 - Create Site
  startSiteTest();
  sleep(2);
  startFreeTrailTest();
}

However,
postman[Symbol.for("initial")]({

has to be declared once. It is mandatory that it contains VU options. Due to this, I cannot define options, VU stages, threshold inside individual JS files.

I want something like below to achieve my goal. However, it is not working.
Inside Start.js –
I will add
postman[Symbol.for("initial")]({

Here I will define all the environment variables.

In each individual JS file, lets say test.js I will define following.

    import http from "k6/http";

    import * as testData from "../../TestData/siteCRUDOptions.js";

    export let options = {
      stages: [
        { duration: "10s", target: 2 },
        { duration: "10s", target: 3 },
      ]
    }


    export default function() {
      console.log("ENV " +  `${__ENV.AUTH0_BASE_URL}`);
      http.get("http://test.loadimpact.com");
    };

Now, I will call start.js from Git.
However, with this approach VU would not get spanned. It seems not allowed. Everytime, test.js or any other file gets called only with 1 VU and 1 Iteration.

APPROACH 2

Create shell script, lets say start.sh. It contains all the JS files calls. Invoke start.js from Git, define it in yml file

k6 run PostmanScripts/Test/Site/test.js
k6 run PostmanScripts/Test/Site/test2.js

APPROACH 3

Write a docker compose script. This would start up a docker container for each test and run it inside there

    services:
      k6_test:
        image: loadimpact/k6
        container_name: test_k6
        command: k6 run /tests/test_spec.js

      k6_test2:
        image: loadimpact/k6
        container_name: test2_k6
        command: k6 run /tests/test2_spec.js

Which would be the better approach? Is there any other way?

Adding @Murat

Thank you for posting ! :pray:t3:

Let me see if I got this right. You’d like to be able to split your tests into multiple files and execute them as a test suite, right?

You’re on the right track with both of your alternatives, however; only the default function and options of the entry file will be read, as you’ve noticed. To go around this, you’d need to manually execute the default function of the other test files from your entry files default function.

I’ve provided a working example below, showcasing what you’re trying to accomplish.

start.js

import http from "k6/http";
import { sleep, check } from "k6";
import runTestOne from "./test1.js";
import runTestTwo from "./test2.js";

export default function() {
    runTestOne();
    runTestTwo();
};

It imports the exported default function from each test file and runs them as part of the default function of start.js. Your actual test files would then look a little something like this:

test1.js

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

export default function() {
  let res = http.get("http://test.loadimpact.com");
  sleep(1);

  check(res, {
      "status is 200": (r) => r.status === 200,
      "response body": (r) => r.body.indexOf("Feel free to browse")
  });
}

test2.js

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

export default function() {
  let res = http.get("http://something-else.example.com");
  sleep(1);

  check(res, {
      "status is 200": (r) => r.status === 200,
      "response body": (r) => r.body.indexOf("Im something completely different")
  });
}

Feel free to reply to this thread if you need any additional assistance. :+1:t2:

how about the test1.js and test2.js options function, how can i set the distribution of test1.js to run stagesA and test2.js to run stagesB?

You currently can’t at least not easily. You can try to approximate things by keeping time internally in the VUs, but it will probably be a bit flaky. https://github.com/loadimpact/k6/issues/1342 is a proposal in how we can quickly implement that soon after https://github.com/loadimpact/k6/pull/1007 is done.

1 Like

@simme thanks a lot!! is there also the possibility to return parameters from runTestOne() and pass as parameter to runTestTwo()? For example: first test is a login script and it returns accessToken to be passed to the other functions

No worries!

Is there also the possibility to return parameters from runTestOne() and pass as parameter to runTestTwo() ? For example: first test is a login script and it returns accessToken to be passed to the other functions

Sure! Just return it from runTestOnce and assign it to a const or let as part of the logic in the default function.

1 Like

thanks!! With this possibility i can make my tests more modular.

1 Like

I found that export function setup() is never called if it specified in test1.js or test2.js
Is it expected behavior?

Correct! The same goes for the setup function. Only the one exported from the entrypoint will be executed. If you want to split it up per test, you’ll have to do it the same way as with the different test functions, that is, import it into your entrypoint and run it as part of that setup function, like:

import { setup as setupOne } from './test1.js'
import { setup as setupTwo } from './test2.js'

export function setup() {
  setupOne();
  setupTwo();
}

The multi-scenario capabilities of the new k6 v0.27.0 should make this much more natural: