Cannot export and import json object in version 0.40.0 but in 0.39.0

I’m trying to export a “json object” defined in my main k6 file and import it in another js file located in a subfolder. This used to work fine in version 0.39.0 of k6 but in 0.40.0 that stopped working for some reason, something in k6 program code must have broken this.

dir structure (k6example.load.js is main file):

C:\k6example\k6example.load.js
C:\k6example\k6example.js
C:\k6example\utils\util-functions.js

Running below causes the error:

k6 run .\k6example.load.js --env TEST_ENVIRONMENT="Performance1"

Error message:

ERRO[0000] TypeError: Cannot read property 'environmentTestDataFile' of undefined
        at getRandomTestUser (file:///C:/k6example/utils/util-functions.js:8:57(8))
        at file:///C:/k6example/k6example.js:6:13(41)
        at go.k6.io/k6/js.(*InitContext).Require-fm (native)
        at file:///C:/k6example/k6example.load.js:1:0(17)
        at native  hint="script exception"

Files in my test project below


k6example.load.js

import { TestRequest } from "./k6example.js";
let testEnvironment = __ENV.TEST_ENVIRONMENT;
let baseURL = "";

switch (testEnvironment) {
case 'Performance1':
baseURL = "https://my.rest.endpoint";
break;
default:
fail('ERROR No parameter for test environment given (TEST_ENVIRONMENT) or environment not supported (supported: Performance1), quitting');
}

export let environment = {
environmentBaseURL: baseURL,
environmentTestDataFile: "./Data/testdata.json",
};

export default function() {
TestRequest();
}

k6example.js

import { group, check } from 'k6';
import http from 'k6/http';
import { getRandomTestUser } from './utils/util-functions.js';
import { environment } from './k6example.load.js';

const user = getRandomTestUser();

export function TestRequest() {
    group("TestRequest", function() {  
        let res, result, params;

        params = 
        {
            headers: {
                        "accept": "application/json",
                    },  
        };
        console.log("Base URL: " + environment.environmentBaseURL);
        res = http.get(environment.environmentBaseURL + "/internal/MyRestFunction", params);
    });
}

util-functions.js

import { environment } from '../k6example.load.js'; //those are only two dots in dir tree, for some reason the message board adds one more dot

export function getRandomTestUser() {
	//func for getting random user from test data file environment.environmentTestDataFile
	// .
	// .
	let user = "12345679";
	console.log("using this data file path: " + environment.environmentTestDataFile);
    return user;
}

Hi @Lightlore,

So first let’s see why this can’t work:

So you run k6example.load.js which loads it and the first thing it does is import k6example.js - so far so good.
k6example.js does import a bunch of different things which we will ignore and then executes

const user = getRandomTestUser();

which is in itself defined in util-functions.js
that imports k6/example.load.sj on it’s own and uses one of its exports. The thing is by definition modules are evaluated only once so the second time somethign imports a module it gets the exports object in it’s current state. It will get updated with all future changes but it won’t have the final values (some asterisks apply).

So at this point due to how module imports work the lines that actually populate environment.environmentTestDataFile have not ran. As they are still “waiting” on the import of k6example.js to end.

If you happened to call getRandomTestUser after it was executed that would not be a problem (for example as part of TestRequest.

Given the particulars of the example I would recommedn making a new
environment.js:

let testEnvironment = __ENV.TEST_ENVIRONMENT;
export let baseURL = "";

switch (testEnvironment) {
case 'Performance1':
baseURL = "https://my.rest.endpoint";
break;
default:
fail('ERROR No parameter for test environment given (TEST_ENVIRONMENT) or environment not supported (supported: Performance1), quitting');
}

export let environment = {
environmentBaseURL: baseURL,
environmentTestDataFile: "./Data/testdata.json",
};

and importing it wherever you want. As it is a “leaf” of the import graph (doesn’t import anything) it will always be executed fully before it is actually used by anything.

NOTE: k6 currently does not natively support import/export it uses babel internally to do the transpilation but AFAIK it won’t work either way. Also technically import don’t get “executed” … but I digress, that is not important.

“Why it works in v0.39.0?” you might ask

Previous to Contain main module within it's own scope as well by mstoykov · Pull Request #2571 · grafana/k6 · GitHub it used to be that main modules specifically would not enter the internal cache that keeps what each module exports before it finishes evaluating.

This means that in your code when the code actually sees import ... from "k6example.load.js" instead of getting the same exports object it will evaluate the whole thing again.

The only reason this doesn’t loop is that at that point the cycle is complete and the internal cache structure just returns the cached exports of k6example.js.

I hope that helps you understand it and does help you now and in the future :wink:

Hi,
Thank you very much for that explanation. I solved it with an environment.js file imported where I needed it like you said.

Have a great day.

/Fredrik