Skip to content

Commit

Permalink
update readme, include content-type headers in requests
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelgoeke committed May 2, 2024
1 parent 2951a70 commit 84a0a58
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# k6-script-from-har

Dead simple k6 test generator.
Given a har file, this script scrapes and generates idiomatic k6 test code.
Given a har file, this script parses and generates cleanly factored k6 test code.
[Only `fetch` calls]

Create a .har file by opening a browser's network tab, navigate, click `export HAR` button. Or can be combined with tools like `Playwright` to have automated tests generate the .har file.
This is an alternative to the [har-to-k6](https://k6.io/docs/test-authoring/create-tests-from-recordings/using-the-har-converter/
) converter which in our experience created messy generated "write only" code that required a lot of clean up every time we used it.

Includes `testTemplate.js` and `testCommon.js` as a starting factoring of shared structure. May be modified after install to your liking. Includes example for getting Auth bearer tokens and attaching them to requests.
## Who this is for
We use this for **workflow** load testing of a backend heavy SPA with 10s or 100s of users.

We wanted to measure
1. Realistic load characteristics (real-world usage conditions)
2. Workflow completion time
* baseline time for one virtual user
* how far can we push virtual user count until the time is unacceptable (e.g. 20s for a multi-page workflow)
3. Infrastructure reliability
* at how many virtual users does something in the infrastructure break (follow up - what broke, how do we harden our infra)

The record and playback approach was a good fit for us. We also wanted basic checks in place e.g. failure response status should show up red instead of green in results.

## Installation
Ensure k6 is [installed](https://grafana.com/docs/k6/latest/set-up/install-k6/)
Expand Down Expand Up @@ -75,3 +88,29 @@ The included testCommon.js and testTemplate.js files include helpers and reasona
* without these checks the performance tests could appear to succeed even though individual calls were beginning to fail, or take a long time.
* Review the contents of testCommon.js for more details

## Example code output

```ts
import { httpRequest, commonSetup, state } from '/testCommon.js';

const given_vus = __ENV.AT_VU_COUNT === undefined ? 15 : __ENV.AT_VU_COUNT;
const given_iterations = __ENV.AT_ITERATIONS === undefined ? given_vus * 3 : __ENV.AT_ITERATIONS;

export const options = {
vus: given_vus,
iterations: given_iterations,
};

export function setup() {
commonSetup();
return state;
}

export default function (setup_state) {
Object.assign(state, setup_state);
httpRequest('GET', 'https://status.k6.io/api/v2/status.json');
httpRequest('GET', 'https://k6.io/data/jobs-positions.json');
}


```
2 changes: 1 addition & 1 deletion bin/k6scriptfromhar.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var allRequests = har.log.entries
.filter(e => e._resourceType === 'fetch')
.map(e => e.request);

const k6TestCodeLines = allRequests.map(r => `httpRequest('${r.method}','${r.url}'${r.postData ? ', ' + JSON.stringify(JSON.parse(r.postData.text)) : ''});`);
const k6TestCodeLines = allRequests.map(r => `httpRequest('${r.method}', '${r.url}'${(!r.postData ? '' : `, ${JSON.stringify(r.postData.text)}, { headers: {'Content-Type': '${r.postData.mimeType}'} }`)});`);
const testBody = k6TestCodeLines.join('\n\t');

var testTemplate = fsSync.readFileSync("./testTemplate.js", {encoding: 'utf8'});
Expand Down
12 changes: 9 additions & 3 deletions src/testCommon.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@ import { check } from 'k6';

export const state = {};
export function httpRequest(method, url, body, params = {}) {
//attach Authorization header if access_token is set
if (state.access_token) params.headers = Object.assign({ "Authorization": 'Bearer ' + state.access_token }, params.headers ? params.headers : {});

//default to JSON content type for POST requests
if (method === 'POST')
params.headers = Object.assign({ "Content-Type": "application/json-patch+json", "Accept": "*/*" }, params.headers ? params.headers : {});
params.timeout = (params.timeout !== undefined ? params.timeout : 600000); //default to 10m timeout instead of 60s implicit default

const res = http.request(method, url, JSON.stringify(body), params);

if (!(res.status >= 200 && res.status < 400)) {
const isSuccessCode = (res.status >= 200 && res.status < 400);
if (!isSuccessCode) {
//log to enable troubleshooting after the test run
const fileAndLineNumber = new Error().stack.split('\n')[2];
console.log('\n\n' + fileAndLineNumber + ' ' + res.url);
console.log({ res });
}

//some reasonable default checks, feel free to modify
const checks = {};
checks[url + ' status 200-399'] = r => r.status >= 200 && r.status < 400;
checks[url + ' status ' + res.status] = r => isSuccessCode;
checks[url + ' timing < 10s'] = r => r.timings.duration < 10000;
check(res, checks);
}
Expand Down

0 comments on commit 84a0a58

Please sign in to comment.