Skip to content

Commit 3af4d26

Browse files
authored
Documentation for jslib.k6.io (#230)
* jslib documentation * httpx lib docs * expect lib docs
1 parent 2b5ce56 commit 3af4d26

35 files changed

+1597
-2
lines changed

.vale/Vocab/docs/accept.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,4 @@ walkthrough
128128
webpages
129129
wpnonce
130130
(?i)codeless
131+
ava
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: "jslib"
3+
excerpt: "External JavaScript libraries for k6"
4+
---
5+
6+
The [jslib.k6.io](https://jslib.k6.io/) is a collection of external JavaScript libraries that can be [directly imported](https://k6.io/docs/using-k6/modules#remote-http-s-modules) in k6 scripts.
7+
8+
9+
| Library | Description |
10+
| -------- | ----------- |
11+
| [expect](/javascript-api/jslib/expect) | Micro-framework for writing tests in a style of Jest or ava. |
12+
| [httpx](/javascript-api/jslib/httpx) | Wrapper around the http that simplifies session handling |
13+
| - | Documentation for other libraries will be added shortly. |
14+
15+
16+
17+
### Example
18+
19+
<CodeGroup labels={[]}>
20+
21+
```javascript
22+
import { check, sleep } from "k6";
23+
import jsonpath from "https://jslib.k6.io/jsonpath/1.0.2/index.js"
24+
import { randomIntBetween,
25+
randomItem,
26+
uuidv4 } from "https://jslib.k6.io/k6-utils/1.0.0/index.js";
27+
28+
const testData = {
29+
user: {
30+
name: "Batman"
31+
}
32+
};
33+
34+
export default function() {
35+
check(testData, {
36+
"JSON path works": () => jsonpath.value(testData, 'user.name') === "Batman"
37+
});
38+
39+
console.log(uuidv4());
40+
console.log(randomItem([1,2,3,4]));
41+
42+
sleep(randomIntBetween(1,5)); // sleep between 1 and 5 seconds
43+
}
44+
```
45+
46+
</CodeGroup>
47+
48+
The complete list of supported libraries can be viewed on [jslib.k6.io](https://jslib.k6.io).
49+
50+
## Versioning
51+
52+
```
53+
https://jslib.k6.io/library-name/version/index.js
54+
```
55+
56+
Libraries hosted on jslib have versions. For example "httpx.js" library currently has v0.0.1, v0.0.2 and v0.0.3.
57+
58+
We recommend that you use the last version available at the time of writing your k6 scripts. Older versions will continue to be hosted on jslib, so you don't have to worry about your scripts breaking.
59+
60+
This documentation is for the last version of these libraries. If the examples documented here don't work, please check that you are using the latest version.
61+
62+
If you don't want to depend on jslib or want to make modifications to the code, you can download the libraries and use them locally.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
title: "httpx"
3+
excerpt: "httpx is a wrapper library around the native k6 http module"
4+
---
5+
6+
The `httpx` module is an external JavaScript library that wraps around the native [k6/http](/javascript-api/k6-http) module.
7+
It's a http client with features that are not yet available in the native module.
8+
- ability to set http options globally (such as timeout)
9+
- ability to set default tags and headers that will be used for all requests
10+
- more user-friendly arguments to request functions (get, post, put take the same arguments)
11+
12+
13+
14+
httpx module integrates well with expect library.
15+
16+
17+
<Blockquote mod='warning'>
18+
19+
#### This library is in active development
20+
21+
This library is stable enough to be useful, but pay attention to the new versions released in jslib.k6.io.
22+
23+
This documentation is for the last version only. If you discover that some of the code below does not work, it most likely means that you are using an older version.
24+
25+
</Blockquote>
26+
27+
28+
### Example
29+
30+
<CodeGroup labels={["httpx session"]}>
31+
32+
```javascript
33+
import { fail } from 'k6';
34+
import { Httpx } from 'https://jslib.k6.io/httpx/0.0.3/index.js';
35+
import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.0.0/index.js";
36+
37+
const USERNAME = `user${randomIntBetween(1, 100000)}@example.com`; // random email address
38+
const PASSWORD = 'superCroc2021';
39+
40+
let session = new Httpx({
41+
baseURL: 'https://test-api.k6.io',
42+
headers: {
43+
'User-Agent': "My custom user agent",
44+
"Content-Type": 'application/x-www-form-urlencoded'
45+
},
46+
timeout: 20000 // 20s timeout.
47+
});
48+
49+
export default function testSuite() {
50+
51+
let registrationResp = session.post(`/user/register/`, {
52+
first_name: 'Crocodile',
53+
last_name: 'Owner',
54+
username: USERNAME,
55+
password: PASSWORD,
56+
});
57+
58+
if (registrationResp.status !== 201){
59+
fail("registration failed")
60+
}
61+
62+
let loginResp = session.post(`/auth/token/login/`, {
63+
username: USERNAME,
64+
password: PASSWORD
65+
});
66+
67+
if(loginResp.status !== 200){
68+
fail("Authentication failed");
69+
}
70+
71+
let authToken = loginResp.json('access');
72+
73+
// set the authorization header on the session for the subsequent requests.
74+
session.addHeader('Authorization', `Bearer ${authToken}`);
75+
76+
let payload = {
77+
name: `Croc Name`,
78+
sex: "M",
79+
date_of_birth: '2019-01-01',
80+
};
81+
82+
// this request uses the Authorization header set above.
83+
let respCreateCrocodile = session.post(`/my/crocodiles/`, payload);
84+
85+
if(respCreateCrocodile.status !== 201){
86+
fail("Crocodile creation failed");
87+
}
88+
else{
89+
console.log("New crocodile created");
90+
}
91+
}
92+
93+
```
94+
95+
</CodeGroup>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
title: "expect"
3+
excerpt: "Functional testing and specifying robust expectations with k6"
4+
---
5+
6+
The `expect` module is a JavaScript library that simplifies specifying expecatation about the responses from the target system. The design of the `expect` library was inspired by ava, Jest and Jasmine. If you already know one of these frameworks, using this library should be very simple.
7+
8+
This library is especially useful for:
9+
- Functional testing, where many asserts are needed
10+
- Stress testing, where the System Under Test is failing and the test code needs to stay robust.
11+
- Load testing, where the failures of the System Under Test need to be robustly collected for analysis
12+
13+
14+
<Blockquote mod='warning'>
15+
16+
#### This library is rapidly evolving.
17+
18+
This library is stable enough to be useful, but pay attention to the new versions released in jslib.k6.io.
19+
20+
This documentation is for the last version only. If you discover that some of the code below does not work, it most likely means that you are using an older version.
21+
22+
</Blockquote>
23+
24+
## Installation
25+
There's nothing to install. This library is hosted on [jslib](https://jslib.k6.io/) and can be imported in the k6 script directly.
26+
27+
<CodeGroup labels={[]}>
28+
29+
```javascript
30+
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js';
31+
```
32+
33+
</CodeGroup>
34+
35+
Alternatively, you can use a copy of this file stored locally.
36+
37+
## Simple example
38+
39+
Let's get started by writing a test for a hypothetical HTTP API that should return a JSON array of objects.
40+
41+
First, create a `mytest.js` k6 script file.
42+
43+
44+
<CodeGroup labels={[]}>
45+
46+
```javascript
47+
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js';
48+
import http from 'k6/http';
49+
50+
export default function testSuite() {
51+
describe('Basic API test', (t) => {
52+
let response = http.get("https://test-api.k6.io/public/crocodiles")
53+
54+
t.expect(response.status).as("API status code").toEqual(200);
55+
})
56+
}
57+
```
58+
59+
</CodeGroup>
60+
61+
If you are familiar with k6, this is similar to using the built-in `group` and `check` functionality but with different names.
62+
63+
When you run this test with `k6 run mytest.js` the result should look similar to this:
64+
65+
```
66+
█ Basic API test
67+
✓ response status is 200.
68+
```
69+
70+
This basic example is not very exciting because the same result can be achieved with `group` and `check`, so let's move on to more interesting examples.
71+
72+
### Chain of checks
73+
74+
When writing integration tests and performance test, it's often necessary to execute conditional checks. For example, you may want to inspect the JSON body only when the http response is 200. If it's 500, the body is not relevant and should not be inspected.
75+
76+
It's possible to chain checks using the `.and()` function, as shown below.
77+
78+
<CodeGroup labels={[]}>
79+
80+
```javascript
81+
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js';
82+
import http from 'k6/http';
83+
84+
export default function testSuite() {
85+
86+
describe('Fetch a list of public crocodiles', (t) => {
87+
let response = http.get("https://test-api.k6.io/public/crocodiles")
88+
89+
t.expect(response.status).as("response status").toEqual(200)
90+
.and(response).toHaveValidJson()
91+
.and(response.json().length).as("number of crocs").toBeGreaterThan(5);
92+
})
93+
}
94+
```
95+
96+
</CodeGroup>
97+
98+
When you run this test with `k6 run mytest.js`, the result should look similar to this:
99+
100+
The above script should result in the following being printed after execution:
101+
102+
```
103+
█ Fetch a list of public crocodiles
104+
✓ response status is 200.
105+
✓ https://test-api.k6.io/public/crocodiles has valid json response
106+
✓ number of crocs is greater than 5
107+
```
108+
109+
More advanced examples can be found in the [examples section](/examples/functional-testing)
110+
111+
112+
| Function | Description |
113+
| -------- | ----------- |
114+
| [describe(name, function)](/javascript-api/jslib/expect/describe-name-function) | Entry point for creating tests. |
115+
| [expect(value)](/javascript-api/jslib/expect/expect-value) | expect(value) sets the value to be used in comparison by the next function in the chain |
116+
| [and(value)](/javascript-api/jslib/expect/and-value) | and(value) is similar to expect(value), but can be used in a chain. |
117+
| [as(alias)](/javascript-api/jslib/expect/as-string) | as(alias) sets a textual representation of the value passed to `expect` or `and`. |
118+
| [toEqual(value)](/javascript-api/jslib/expect/toequal-expectedvalue) | The `.toEqual(expectedValue)` is similar to `===` |
119+
| [toBeGreaterThan(expectedValue)](/javascript-api/jslib/expect/tobegreaterthan-expectedvalue) | Use to verify that `received` > `expected` |
120+
| [toBeGreaterThanOrEqual(expectedValue)](/javascript-api/jslib/expect/tobegreaterthanorequal-expectedvalue) | Use to verify that `received` >= `expected` |
121+
| [toBeLessThan(expectedValue)](/javascript-api/jslib/expect/tobelessthan-expectedvalue) | Use to verify that `received` < `expected` |
122+
| [toBeLessThanOrEqual(expectedValue)](/javascript-api/jslib/expect/tobelessthanorequal-expectedvalue) | Use to verify that `received` <= `expected` |
123+
| [toBeBetween(from, to)](/javascript-api/jslib/expect/tobebetween-from-to) | Use to verify that expected value is within range. |
124+
| [toBeTruthy()](/javascript-api/jslib/expect/tobetruthy) | Use `.toBeTruthy` when you don't care what a value is and you want to ensure a value is true in a boolean context. |
125+
| [toHaveValidJson()](/javascript-api/jslib/expect/tohavevalidjson) | Use to verify that the http response has a valid JSON body. |
126+
127+
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
title: 'Error handling in expect.js'
3+
description: 'How to handle errors in expect.js.'
4+
---
5+
6+
When executing a performance or integration test, you should expect that your system under test may crash. If this happens, your test should print useful information rather than stack traces caused by unexpected HTTP responses.
7+
8+
`expect` library is designed to make it easy to write test scripts that are resilient to failing SUT (System Under Test).
9+
10+
It's not uncommon for performance testers to write fragile code that assumes the http response will contain expected data.
11+
12+
Fragile code is most clearly demonstrated with an example.
13+
14+
<CodeGroup labels={["Test code that is fragile to failing SUT"]}>
15+
16+
```javascript
17+
import { check, group } from 'k6';
18+
import http from 'k6/http';
19+
20+
export default function() {
21+
group("Fetch a list of public crocodiles", () => {
22+
let res = http.get("https://test-api.k6.io/public/crocodiles");
23+
24+
check(res, {
25+
'is status 200': (r) => r.status === 200,
26+
'got more than 5 crocs': (r) => r.json().length > 5,
27+
});
28+
})
29+
// more code here
30+
}
31+
```
32+
33+
</CodeGroup>
34+
35+
36+
This code will work fine as long as SUT (System Under Test) returns correct responses. When the SUT starts to fail, there's a good chance the `r.json().length` will throw an exception similar to
37+
38+
```bash
39+
ERRO[0001] GoError: cannot parse json due to an error at line 1, character 2 , error: invalid character '<' looking for beginning of value
40+
running at reflect.methodValueCall (native)
41+
default at gotMoreThan5Crocs (file:///home/user/happy-path-check.js:7:68(5))
42+
at github.com/loadimpact/k6/js/common.Bind.func1 (native)
43+
at file:///home/user/happy-path-check.js:5:22(17) executor=per-vu-iterations scenario=default source=stacktrace
44+
```
45+
46+
In this example, the system was overloaded, and the load balancer returned a 503 response that did not have a valid JSON body. k6 has thrown a JavaScript exception and restarted execution from the beginning.
47+
This test code is fragile to failing SUT because the first `check` does not prevent the second check from executing.
48+
It's possible to rewrite this code to be less fragile, but that will make it longer and less readable.
49+
50+
Error handling of this type happens automatically when using the `expect.js` library.
51+
When the first `expect` fails, the remaining checks in the chain are not executed, and the test is marked as failed — the execution proceeds to the next `describe()` instead of restarting from the top.
52+
53+
54+
<CodeGroup labels={["Resilient code written using expect.js"]}>
55+
56+
```javascript
57+
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js';
58+
import http from 'k6/http';
59+
60+
export default function() {
61+
describe('Fetch a list of public crocodiles', (t) => {
62+
let response = http.get("https://test-api.k6.io/public/crocodiles")
63+
64+
t.expect(response.status).as("response status").toEqual(200)
65+
.and(response).toHaveValidJson()
66+
.and(response.json().length).as("number of crocs").toBeGreaterThan(5);
67+
})
68+
// more code here
69+
}
70+
```
71+
72+
</CodeGroup>
73+
74+
# Handling exceptions
75+
76+
Sometimes it's hard to predict the way SUT can fail. For those cases, the `expect` library caught any exceptions thrown inside of `describe()` body, and records it as a failed condition.
77+
78+
<CodeGroup labels={[]}>
79+
80+
```javascript
81+
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js';
82+
import http from 'k6/http';
83+
84+
export default function testSuite() {
85+
86+
describe('Executing test against a Shaky SUT', (t) => {
87+
throw("Something entirely unexpected happened");
88+
});
89+
}
90+
```
91+
92+
</CodeGroup>
93+
94+
Execution of this script should print the following output.
95+
96+
97+
![output](./images/exception-handling.png)
98+

0 commit comments

Comments
 (0)