Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating a global variable #2267

Closed
mmonfared opened this issue Nov 29, 2021 · 1 comment
Closed

Creating a global variable #2267

mmonfared opened this issue Nov 29, 2021 · 1 comment
Labels

Comments

@mmonfared
Copy link

Assuming that I want to test our SignUp endpoint and I'm going to Sign up more than 10 users in 10 iterations at once.
Every HTTP request for signup will return a unique ID for the created user which we called customer_id.
I want when whole the test is done, delete all the new users which are created by our test, in the teardown().
I already read the documentation and search a lot, but cannot find any way to reach this.

This is what I mean (using init section):

import {signUpUser, deleteCustomer} from "../helpers";


export const options = {
    vus: 10,
    iterations: 10
};

let customer_list = null

export default function () {
    let customer_id = signUpUser()
    customer_list.push(customer_id)
}

export function teardown() {
    for (const cus_id in customer_list) {
        deleteCustomer(cus_id)
    }
}

Also, I have tested using setup(), neither works.

import {signUpUser, deleteCustomer} from "../helpers";


export const options = {
    vus: 10,
    iterations: 10
};

export function setup() {
    let customer_list = null
    return customer_list
}

export default function (data) {
    let customer_id = signUpUser()
    data.push(customer_id)
}

export function teardown(data) {
    for (const cus_id in data) {
        deleteCustomer(cus_id)
    }
}
@na--
Copy link
Member

na-- commented Nov 29, 2021

In k6, the iterations (the default function invocations) are executed in completely different JavaScript runtimes from each other, and from the setup() and teardown() function. Every VU (virtual user) is a separate JavaScript runtime. So the customer_list variable isn't actually shared among all of the runtimes, every VU has its own local copy of it. setup() and teardown() are executed in transient VUs, so they also have their own unique (and empty) copy of the list...

Similarly, the data that is returned from setup() function and passed as the first argument to the default function and teardown is immutable. You can't add to it, since that would be a data race when all of the VUs try to simultaneously write to it (not to mention how it would work on multiple machines).

In short, you can't use setup() and teardown() to do what you want to do, unless you create all of the users in setup(). But, from what I understand, you actually want to load test the user creation, so that's probably not suitable. Instead, you can use multiple scenarios to achieve your goals: https://k6.io/docs/using-k6/scenarios/

You can use the fact that scenarios have a startTime property, and if two different scenarios do not overlap, k6 will reuse VUs between them. So, if every VU populates its own customer_list with the created customers in the first scenario, it can delete them in the second one. Here's a simplified example of how this can be implemented:

import { sleep } from 'k6';
import exec from 'k6/execution';
import http from 'k6/http';
import { randomString } from 'https://jslib.k6.io/k6-utils/1.1.0/index.js';

const totalVUs = 10;

export let options = {
    scenarios: {
        // The executor here doesn't matter, as long as you make sure that its
        // maximum duration + gracefulStop value is less than the startTime of
        // the second scenario, and that they have the same number of VUs
        create_users: {
            executor: 'constant-arrival-rate',
            preAllocatedVUs: totalVUs,
            rate: 10,
            duration: '13s',
            gracefulStop: '2s',
            exec: 'createUser',
        },

        cleanup: {
            executor: 'per-vu-iterations',
            vus: totalVUs,
            iterations: 1, // we only need one iteration to clean everything up
            startTime: '15s', // equal to duration + gracefulStop of create_users
            exec: 'cleanupUsers',
        },
    },
};

let customerList = [];

export function createUser() {
    let customerName = `user-${exec.scenario.iterationInTest}-${randomString(10)}`;
    console.log(`VU {${exec.vu.idInTest}} created customer ${customerName}...`);
    // TODO: actually create it :)
    customerList.push(customerName);
};

export function cleanupUsers() {
    // customerList here will be local for this VU, populated with just the
    // users that the same VU created in the previous scenario.
    customerList.forEach(customerName => {
        console.log(`VU {${exec.vu.idInTest}} deleted customer ${customerName}!`);
        // TODO: actually delete it...
    });
};

I hope this helps, and I've opened a new issue to explain these concepts better in our docs, since it's obvious https://k6.io/docs/using-k6/test-life-cycle/ is not quite enough: grafana/k6-docs#512. And an issue to have this workaround as an official example: grafana/k6-docs#513

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants