Created
November 16, 2020 20:19
-
-
Save zanonnicola/41d0d983f6b9cb4787278af53bfc659a to your computer and use it in GitHub Desktop.
K6 Load test example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { group, sleep, check } from "k6"; | |
import { parseHTML } from 'k6/html'; | |
import { Trend } from "k6/metrics"; | |
import http from "k6/http"; | |
import { randomIntBetween, uuidv4 } from "https://jslib.k6.io/k6-utils/1.0.0/index.js"; | |
const usersToken = JSON.parse(open("./users.json")); | |
const URL = "https://myapp.com"; | |
const TEST_NAME = "First Test - simple user flow"; | |
function createRequestsObject(staticPaths, url, params) { | |
return staticPaths.map((url) => { | |
const key = url.slice(1); | |
return { | |
[key]: { | |
method: "GET", | |
url, | |
params: Object.assign({}, params, { tags: { type: 'static-asset' } }), | |
} | |
} | |
}).reduce(((r, c) => Object.assign(r, c)), {}); | |
} | |
function generateCorrelationId(runId, userId) { | |
return `k6-${runId}-${userId}-${uuidv4()}`; | |
} | |
function generateTestId(testName) { | |
return testName.toLowerCase().replace(/\s+/g, ""); | |
} | |
/* | |
* | |
* START K6 | |
* | |
*/ | |
export let options = { | |
stages: [ | |
{ duration: "1m", target: 50 }, | |
{ duration: "3m", target: 50 }, | |
{ duration: "1m", target: 0 }, | |
], | |
thresholds: { | |
error_rate: ["rate < 0.9"], | |
http_req_duration: ["p(95)<2000"], | |
"time_to_first_byte{type:static-assets}": ["p(95)<500"], | |
}, | |
ext: { | |
loadimpact: { | |
projectID: 3492606, | |
name: TEST_NAME, | |
distribution: { | |
"amazon:us:ashburn": { loadZone: "amazon:us:ashburn", percent: 100 } | |
} | |
} | |
} | |
}; | |
const timeToFirstByte = new Trend("time_to_first_byte", true); | |
export function setup() { | |
// Set up code that we can pass down results to the main function | |
// Called only once per test | |
// Return any data if you want to be use it in the next steps | |
// Collecting static resources paths ( Doesn't matter which user, static assets will be the same) | |
const res = http.get(URL, { | |
cookies: Object.assign({}, usersToken[0].cookies), | |
headers: { | |
"X-Requested-With": "XMLHttpRequest", | |
"x-csrf-token": usersToken[0].cookies["XSRF-TOKEN"], | |
"Accept": "application/json", | |
} | |
}); | |
// Extracting the paths from <link /> elements | |
// Vue uses link prefetch with all the static assets | |
const doc = parseHTML(res.body); | |
const assets = doc.find('link'); | |
const staticAssetsPaths = []; | |
assets.each((idx, el) => { | |
const path = el.getAttribute("href"); | |
if ( | |
path.startsWith("/css") || | |
path.startsWith("/js") || | |
path.startsWith("/fonts") | |
) { | |
staticAssetsPaths.push(URL + path); | |
} | |
}); | |
return staticAssetsPaths; | |
} | |
export default function (staticAssetsPaths) { | |
// You can't import any files here. Use the global scope to import modules or data. | |
// The below code will be run in loop for the amount of Virtual Users that we have specified. | |
// Select random user | |
const user = Math.floor(Math.random() * usersToken.length); | |
// Getting and setting the required cookies for auth calls | |
const { cookies, id } = usersToken[user]; | |
// This will set the cookies for all the requests | |
const jar = http.cookieJar(); | |
Object.entries(cookies).forEach(([key, value]) => { | |
jar.set(URL, key, value); | |
}); | |
// Request headers | |
const params = () => ({ | |
headers: { | |
"X-Requested-With": "XMLHttpRequest", | |
"x-csrf-token": cookies["XSRF-TOKEN"], | |
"Accept": "application/json", | |
"x-correlationid": generateCorrelationId(generateTestId(TEST_NAME), id) // We can easilly track and identify the requests in Kibana/NewRelic/Dynatrace for example | |
} | |
}); // We need to pass params on every requests | |
group("HomePage", () => { | |
const requests = { | |
"user/preference": { | |
method: "GET", | |
url: `${URL}/user/preference`, | |
params: params(), | |
}, | |
"some/endpoint": { | |
method: "GET", | |
url: `${URL}/some/endpoint`, | |
params: params(), | |
}, | |
"another/one": { | |
method: "GET", | |
url: `${URL}/another/one`, | |
params: params(), | |
} | |
}; | |
// Will run requests in parallel (default: 6 at once. Similar to browser behaviour) | |
const responses = http.batch(requests); | |
Object.keys(requests).forEach((reqKey) => { | |
check(responses[reqKey], { | |
"Response status was 200": res => res.status === 200, | |
}); | |
timeToFirstByte.add(responses[reqKey].timings.waiting, { ttfbURL: responses[reqKey].url }); // Use a custom metric | |
}); | |
group("Static assets", function () { | |
const staticAssetsResponses = http.batch(createRequestsObject(staticAssetsPaths, URL, params)); // Helper function to get the abs URL for each asset involved in the test | |
Object.entries(staticAssetsResponses).forEach(([key, value]) => { | |
check(value, { | |
"Static response status was 200": res => res.status === 200, | |
}); | |
timeToFirstByte.add(value.timings.waiting, { ttfbURL: value.url, type: "static-assets" }); | |
}); | |
}); | |
}); | |
// User will probably spend some time looking at the Home page | |
sleep(randomIntBetween(3, 10)); | |
}; | |
export function teardown(data) { | |
// Teardown code | |
// Called only once per test | |
// Data will be whatever is returned in the setup function | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment