In an attempt to keep our load tests from getting stale, we want to automate verifying that the requests made by the k6 tests match what a real user session would experience. Our plan is to have a puppet-based test which actually clicks through a browser, performing the scenario that our load tests are simulating, exporting a HAR…then we run our load test (a single run through), and export the hits (somehow)…then we compare the HAR with the k6 exported hits to determine if they’re getting stale. The closest I can find is to run k6 with --http-debug but that doesn’t give super machine-friendly output (although I’m sure it could be made to work). Does anyone know of any better options to accomplish this?
You can easily wrap the k6/http
module calls in your own object that calls console.log()
on every request. Here’s a short example, if you have a script like this:
import * as k6http from 'k6/http';
var http = {
request: function (method, url, body, params) {
let resp = k6http.request(method, url, body, params);
// TODO: rearrange the fields?
// https://k6.io/docs/javascript-api/k6-http/response
console.log(JSON.stringify(resp));
},
get: function (url, params) {
return http.request('GET', url, null, params);
},
post: function (url, body, params) {
return http.request('POST', url, body, params);
},
// TODO: the other method types
batch: function (requests) {
let responses = k6http.batch(requests);
responses.forEach((r) => {
// TODO: rearrange the fields?
console.log(JSON.stringify(r));
})
},
}
export default function () {
http.get('https://test.k6.io/pi.php?decimals=3');
http.batch([
['GET', 'https://test-api.k6.io/public/crocodiles/1/'],
['GET', 'https://test-api.k6.io/public/crocodiles/2/'],
]);
}
And you launch it with k6 run --logformat=raw --console-output=./log.json script.js
, you’d get something like this in log.json
:
{"remote_ip":"18.205.170.76","remote_port":443,"url":"https://test.k6.io/pi.php?decimals=3","status":200,"proto":"HTTP/2.0","headers":{"X-Powered-By":"PHP/5.6.40","Date":"Wed, 09 Sep 2020 05:36:10 GMT","Content-Type":"text/plain;charset=UTF-8","Server":"nginx/1.14.0"},"cookies":{},"body":"3.141","timings":{"duration":209.221659,"blocked":603.670364,"looking_up":0,"connecting":159.885278,"tls_handshaking":409.825543,"sending":0.217406,"waiting":208.889033,"receiving":0.11522},"tls_version":"tls1.2","tls_cipher_suite":"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","ocsp":{"produced_at":0,"this_update":0,"next_update":0,"revoked_at":0,"revocation_reason":"","status":"unknown"},"error":"","error_code":0,"request":{"method":"GET","url":"https://test.k6.io/pi.php?decimals=3","headers":{"User-Agent":["k6/0.27.1 (https://k6.io/)"]},"body":"","cookies":{}}}
{"remote_ip":"52.54.204.149","remote_port":443,"url":"https://test-api.k6.io/public/crocodiles/1/","status":200,"proto":"HTTP/2.0","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Wed, 09 Sep 2020 05:36:10 GMT","Content-Type":"application/json; charset=utf-8","Content-Length":"70","Server":"nginx","Vary":"Accept"},"cookies":{},"body":"{\"id\":1,\"name\":\"Bert\",\"sex\":\"M\",\"date_of_birth\":\"2010-06-27\",\"age\":10}","timings":{"duration":204.390239,"blocked":0.002635,"looking_up":0,"connecting":0,"tls_handshaking":0,"sending":0.028123,"waiting":204.073181,"receiving":0.288935},"tls_version":"tls1.2","tls_cipher_suite":"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","ocsp":{"produced_at":0,"this_update":0,"next_update":0,"revoked_at":0,"revocation_reason":"","status":"unknown"},"error":"","error_code":0,"request":{"method":"GET","url":"https://test-api.k6.io/public/crocodiles/1/","headers":{"User-Agent":["k6/0.27.1 (https://k6.io/)"]},"body":"","cookies":{}}}
{"remote_ip":"52.54.204.149","remote_port":443,"url":"https://test-api.k6.io/public/crocodiles/2/","status":200,"proto":"HTTP/2.0","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Wed, 09 Sep 2020 05:36:10 GMT","Content-Type":"application/json; charset=utf-8","Content-Length":"68","Server":"nginx","Vary":"Accept"},"cookies":{},"body":"{\"id\":2,\"name\":\"Ed\",\"sex\":\"M\",\"date_of_birth\":\"1995-02-27\",\"age\":25}","timings":{"duration":205.319859,"blocked":609.174697,"looking_up":0,"connecting":132.468748,"tls_handshaking":386.140804,"sending":0.830276,"waiting":204.242604,"receiving":0.246979},"tls_version":"tls1.2","tls_cipher_suite":"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","ocsp":{"produced_at":0,"this_update":0,"next_update":0,"revoked_at":0,"revocation_reason":"","status":"unknown"},"error":"","error_code":0,"request":{"method":"GET","url":"https://test-api.k6.io/public/crocodiles/2/","headers":{"User-Agent":["k6/0.27.1 (https://k6.io/)"]},"body":"","cookies":{}}}
Ooooh that’s a great idea. Thanks!
This solution mostly worked really nice, thanks…however I’m finding I’m missing redirect requests, which is proving to be a problem for me. I can’t figure out how to get the redirect requests…in theory I can disable the redirects in k6 and issue them myself…but that doesn’t work that great for batches, as I would be fundamentally altering how the tests work - I would need to be issuing multiple batch requests, whenever any of the batched requests return a 3XX I would need to issue another one etc…which would work, but it sure isn’t pretty. Is there any other approach which could be used to accomplish this?
oh, boo…it also misses all requests made by response.submitForm()
Hmm yeah, it’s not ideal I am not sure exactly what you need to verify, but maybe the using the metric tags from the JSON output would be enough? If you run k6 run --out json=metric_samples.json
, then jq '. | select(.type=="Point" and .metric == "http_reqs") | .data.tags' metric_samples.json
will result in something like this:
{
"group": "",
"method": "GET",
"name": "https://test-api.k6.io/",
"proto": "HTTP/2.0",
"scenario": "default",
"status": "200",
"tls_version": "tls1.2",
"url": "https://test-api.k6.io/"
}
Yeah, thanks that’s kinda where I started, but I kinda need the request post bodies. I’ve got a working solution now using http-debug + logformat=json…there’s a couple hacks needed, but it’s working. Thanks for your help