-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documentation for jslib.k6.io (#230)
* jslib documentation * httpx lib docs * expect lib docs
- Loading branch information
Showing
35 changed files
with
1,597 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -128,3 +128,4 @@ walkthrough | |
webpages | ||
wpnonce | ||
(?i)codeless | ||
ava |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--- | ||
title: "jslib" | ||
excerpt: "External JavaScript libraries for k6" | ||
--- | ||
|
||
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. | ||
|
||
|
||
| Library | Description | | ||
| -------- | ----------- | | ||
| [expect](/javascript-api/jslib/expect) | Micro-framework for writing tests in a style of Jest or ava. | | ||
| [httpx](/javascript-api/jslib/httpx) | Wrapper around the http that simplifies session handling | | ||
| - | Documentation for other libraries will be added shortly. | | ||
|
||
|
||
|
||
### Example | ||
|
||
<CodeGroup labels={[]}> | ||
|
||
```javascript | ||
import { check, sleep } from "k6"; | ||
import jsonpath from "https://jslib.k6.io/jsonpath/1.0.2/index.js" | ||
import { randomIntBetween, | ||
randomItem, | ||
uuidv4 } from "https://jslib.k6.io/k6-utils/1.0.0/index.js"; | ||
|
||
const testData = { | ||
user: { | ||
name: "Batman" | ||
} | ||
}; | ||
|
||
export default function() { | ||
check(testData, { | ||
"JSON path works": () => jsonpath.value(testData, 'user.name') === "Batman" | ||
}); | ||
|
||
console.log(uuidv4()); | ||
console.log(randomItem([1,2,3,4])); | ||
|
||
sleep(randomIntBetween(1,5)); // sleep between 1 and 5 seconds | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
The complete list of supported libraries can be viewed on [jslib.k6.io](https://jslib.k6.io). | ||
|
||
## Versioning | ||
|
||
``` | ||
https://jslib.k6.io/library-name/version/index.js | ||
``` | ||
|
||
Libraries hosted on jslib have versions. For example "httpx.js" library currently has v0.0.1, v0.0.2 and v0.0.3. | ||
|
||
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. | ||
|
||
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. | ||
|
||
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. |
95 changes: 95 additions & 0 deletions
95
src/data/markdown/docs/02 javascript api/11 jslib/01 httpx.md
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
--- | ||
title: "httpx" | ||
excerpt: "httpx is a wrapper library around the native k6 http module" | ||
--- | ||
|
||
The `httpx` module is an external JavaScript library that wraps around the native [k6/http](/javascript-api/k6-http) module. | ||
It's a http client with features that are not yet available in the native module. | ||
- ability to set http options globally (such as timeout) | ||
- ability to set default tags and headers that will be used for all requests | ||
- more user-friendly arguments to request functions (get, post, put take the same arguments) | ||
|
||
|
||
|
||
httpx module integrates well with expect library. | ||
|
||
|
||
<Blockquote mod='warning'> | ||
|
||
#### This library is in active development | ||
|
||
This library is stable enough to be useful, but pay attention to the new versions released in jslib.k6.io. | ||
|
||
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. | ||
|
||
</Blockquote> | ||
|
||
|
||
### Example | ||
|
||
<CodeGroup labels={["httpx session"]}> | ||
|
||
```javascript | ||
import { fail } from 'k6'; | ||
import { Httpx } from 'https://jslib.k6.io/httpx/0.0.3/index.js'; | ||
import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.0.0/index.js"; | ||
|
||
const USERNAME = `user${randomIntBetween(1, 100000)}@example.com`; // random email address | ||
const PASSWORD = 'superCroc2021'; | ||
|
||
let session = new Httpx({ | ||
baseURL: 'https://test-api.k6.io', | ||
headers: { | ||
'User-Agent': "My custom user agent", | ||
"Content-Type": 'application/x-www-form-urlencoded' | ||
}, | ||
timeout: 20000 // 20s timeout. | ||
}); | ||
|
||
export default function testSuite() { | ||
|
||
let registrationResp = session.post(`/user/register/`, { | ||
first_name: 'Crocodile', | ||
last_name: 'Owner', | ||
username: USERNAME, | ||
password: PASSWORD, | ||
}); | ||
|
||
if (registrationResp.status !== 201){ | ||
fail("registration failed") | ||
} | ||
|
||
let loginResp = session.post(`/auth/token/login/`, { | ||
username: USERNAME, | ||
password: PASSWORD | ||
}); | ||
|
||
if(loginResp.status !== 200){ | ||
fail("Authentication failed"); | ||
} | ||
|
||
let authToken = loginResp.json('access'); | ||
|
||
// set the authorization header on the session for the subsequent requests. | ||
session.addHeader('Authorization', `Bearer ${authToken}`); | ||
|
||
let payload = { | ||
name: `Croc Name`, | ||
sex: "M", | ||
date_of_birth: '2019-01-01', | ||
}; | ||
|
||
// this request uses the Authorization header set above. | ||
let respCreateCrocodile = session.post(`/my/crocodiles/`, payload); | ||
|
||
if(respCreateCrocodile.status !== 201){ | ||
fail("Crocodile creation failed"); | ||
} | ||
else{ | ||
console.log("New crocodile created"); | ||
} | ||
} | ||
|
||
``` | ||
|
||
</CodeGroup> |
127 changes: 127 additions & 0 deletions
127
src/data/markdown/docs/02 javascript api/11 jslib/02 expect.md
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
--- | ||
title: "expect" | ||
excerpt: "Functional testing and specifying robust expectations with k6" | ||
--- | ||
|
||
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. | ||
|
||
This library is especially useful for: | ||
- Functional testing, where many asserts are needed | ||
- Stress testing, where the System Under Test is failing and the test code needs to stay robust. | ||
- Load testing, where the failures of the System Under Test need to be robustly collected for analysis | ||
|
||
|
||
<Blockquote mod='warning'> | ||
|
||
#### This library is rapidly evolving. | ||
|
||
This library is stable enough to be useful, but pay attention to the new versions released in jslib.k6.io. | ||
|
||
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. | ||
|
||
</Blockquote> | ||
|
||
## Installation | ||
There's nothing to install. This library is hosted on [jslib](https://jslib.k6.io/) and can be imported in the k6 script directly. | ||
|
||
<CodeGroup labels={[]}> | ||
|
||
```javascript | ||
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js'; | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
Alternatively, you can use a copy of this file stored locally. | ||
|
||
## Simple example | ||
|
||
Let's get started by writing a test for a hypothetical HTTP API that should return a JSON array of objects. | ||
|
||
First, create a `mytest.js` k6 script file. | ||
|
||
|
||
<CodeGroup labels={[]}> | ||
|
||
```javascript | ||
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js'; | ||
import http from 'k6/http'; | ||
|
||
export default function testSuite() { | ||
describe('Basic API test', (t) => { | ||
let response = http.get("https://test-api.k6.io/public/crocodiles") | ||
|
||
t.expect(response.status).as("API status code").toEqual(200); | ||
}) | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
If you are familiar with k6, this is similar to using the built-in `group` and `check` functionality but with different names. | ||
|
||
When you run this test with `k6 run mytest.js` the result should look similar to this: | ||
|
||
``` | ||
█ Basic API test | ||
✓ response status is 200. | ||
``` | ||
|
||
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. | ||
|
||
### Chain of checks | ||
|
||
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. | ||
|
||
It's possible to chain checks using the `.and()` function, as shown below. | ||
|
||
<CodeGroup labels={[]}> | ||
|
||
```javascript | ||
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js'; | ||
import http from 'k6/http'; | ||
|
||
export default function testSuite() { | ||
|
||
describe('Fetch a list of public crocodiles', (t) => { | ||
let response = http.get("https://test-api.k6.io/public/crocodiles") | ||
|
||
t.expect(response.status).as("response status").toEqual(200) | ||
.and(response).toHaveValidJson() | ||
.and(response.json().length).as("number of crocs").toBeGreaterThan(5); | ||
}) | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
When you run this test with `k6 run mytest.js`, the result should look similar to this: | ||
|
||
The above script should result in the following being printed after execution: | ||
|
||
``` | ||
█ Fetch a list of public crocodiles | ||
✓ response status is 200. | ||
✓ https://test-api.k6.io/public/crocodiles has valid json response | ||
✓ number of crocs is greater than 5 | ||
``` | ||
|
||
More advanced examples can be found in the [examples section](/examples/functional-testing) | ||
|
||
|
||
| Function | Description | | ||
| -------- | ----------- | | ||
| [describe(name, function)](/javascript-api/jslib/expect/describe-name-function) | Entry point for creating tests. | | ||
| [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 | | ||
| [and(value)](/javascript-api/jslib/expect/and-value) | and(value) is similar to expect(value), but can be used in a chain. | | ||
| [as(alias)](/javascript-api/jslib/expect/as-string) | as(alias) sets a textual representation of the value passed to `expect` or `and`. | | ||
| [toEqual(value)](/javascript-api/jslib/expect/toequal-expectedvalue) | The `.toEqual(expectedValue)` is similar to `===` | | ||
| [toBeGreaterThan(expectedValue)](/javascript-api/jslib/expect/tobegreaterthan-expectedvalue) | Use to verify that `received` > `expected` | | ||
| [toBeGreaterThanOrEqual(expectedValue)](/javascript-api/jslib/expect/tobegreaterthanorequal-expectedvalue) | Use to verify that `received` >= `expected` | | ||
| [toBeLessThan(expectedValue)](/javascript-api/jslib/expect/tobelessthan-expectedvalue) | Use to verify that `received` < `expected` | | ||
| [toBeLessThanOrEqual(expectedValue)](/javascript-api/jslib/expect/tobelessthanorequal-expectedvalue) | Use to verify that `received` <= `expected` | | ||
| [toBeBetween(from, to)](/javascript-api/jslib/expect/tobebetween-from-to) | Use to verify that expected value is within range. | | ||
| [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. | | ||
| [toHaveValidJson()](/javascript-api/jslib/expect/tohavevalidjson) | Use to verify that the http response has a valid JSON body. | | ||
|
||
|
98 changes: 98 additions & 0 deletions
98
src/data/markdown/docs/02 javascript api/11 jslib/expect/10 error handling.md
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
--- | ||
title: 'Error handling in expect.js' | ||
description: 'How to handle errors in expect.js.' | ||
--- | ||
|
||
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. | ||
|
||
`expect` library is designed to make it easy to write test scripts that are resilient to failing SUT (System Under Test). | ||
|
||
It's not uncommon for performance testers to write fragile code that assumes the http response will contain expected data. | ||
|
||
Fragile code is most clearly demonstrated with an example. | ||
|
||
<CodeGroup labels={["Test code that is fragile to failing SUT"]}> | ||
|
||
```javascript | ||
import { check, group } from 'k6'; | ||
import http from 'k6/http'; | ||
|
||
export default function() { | ||
group("Fetch a list of public crocodiles", () => { | ||
let res = http.get("https://test-api.k6.io/public/crocodiles"); | ||
|
||
check(res, { | ||
'is status 200': (r) => r.status === 200, | ||
'got more than 5 crocs': (r) => r.json().length > 5, | ||
}); | ||
}) | ||
// more code here | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
|
||
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 | ||
|
||
```bash | ||
ERRO[0001] GoError: cannot parse json due to an error at line 1, character 2 , error: invalid character '<' looking for beginning of value | ||
running at reflect.methodValueCall (native) | ||
default at gotMoreThan5Crocs (file:///home/user/happy-path-check.js:7:68(5)) | ||
at github.com/loadimpact/k6/js/common.Bind.func1 (native) | ||
at file:///home/user/happy-path-check.js:5:22(17) executor=per-vu-iterations scenario=default source=stacktrace | ||
``` | ||
|
||
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. | ||
This test code is fragile to failing SUT because the first `check` does not prevent the second check from executing. | ||
It's possible to rewrite this code to be less fragile, but that will make it longer and less readable. | ||
|
||
Error handling of this type happens automatically when using the `expect.js` library. | ||
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. | ||
|
||
|
||
<CodeGroup labels={["Resilient code written using expect.js"]}> | ||
|
||
```javascript | ||
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js'; | ||
import http from 'k6/http'; | ||
|
||
export default function() { | ||
describe('Fetch a list of public crocodiles', (t) => { | ||
let response = http.get("https://test-api.k6.io/public/crocodiles") | ||
|
||
t.expect(response.status).as("response status").toEqual(200) | ||
.and(response).toHaveValidJson() | ||
.and(response.json().length).as("number of crocs").toBeGreaterThan(5); | ||
}) | ||
// more code here | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
# Handling exceptions | ||
|
||
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. | ||
|
||
<CodeGroup labels={[]}> | ||
|
||
```javascript | ||
import { describe } from 'https://jslib.k6.io/expect/0.0.4/index.js'; | ||
import http from 'k6/http'; | ||
|
||
export default function testSuite() { | ||
|
||
describe('Executing test against a Shaky SUT', (t) => { | ||
throw("Something entirely unexpected happened"); | ||
}); | ||
} | ||
``` | ||
|
||
</CodeGroup> | ||
|
||
Execution of this script should print the following output. | ||
|
||
|
||
![output](./images/exception-handling.png) | ||
|
Oops, something went wrong.