Creating Load Tests with k6
Tags: k6, testing, javascript, performance
Published: 2021-11-23 01:48:14

Creating a scalable application is very important in current days.

But, how can we test our application in hard scenarios?
How can we test performance in scenarios of many users and many requests at the same time?

Let's start understanding the **Load Test**.

### Load Test 

> Load testing generally refers to the practice of modeling the expected usage of a software program by simulating multiple users accessing the program concurrently.

## What is k6?

From official site:

> k6 is a developer-centric, free and open-source load testing tool built for making performance testing a productive and enjoyable experience.

## Install k6

Install k6 on Windows:

``` shell
$ choco install k6
``` 

or on Linux:

``` shell
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
$ echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
$ sudo apt-get update
$ sudo apt-get install k6
``` 

or on MacOS:

``` shell
$ brew install k6
``` 

or follow the steps of the [installation guide](https://k6.io/docs/getting-started/installation/).

## Running k6

For running the tests we will use the following [repository on GitHub](https://github.com/viniciusabreusouza/k6-examples).

In this repository, we have two folders. The `load-test` and `stress-test`.

### Running Load Test

First, we will run the Load Test. So, access the root folder and run the command:

``` shell
$ k6 run load_test\script.js
```

In the code, we can understand the load test:

``` typescript
import http from 'k6/http';
import { sleep, check } from 'k6';

//Base Url for the test
export const BASE_URL = 'https://test-api.k6.io';

export const options = {
  thresholds: {
    http_req_failed: ['rate<0.01'], // http errors should be less than 1%
    http_req_duration: ['p(95)<250'], // 95% of requests should be below 250ms
  },
  stages: [
    { duration: '30s', target: 20 },   // simulate ramp-up of traffic from 1 to 20 users over 30 seconds.
    { duration: '1m30s', target: 10 }, // ramp-down to 10 users over 1 min and 30 sec
    { duration: '20s', target: 0 },    // ramp-down to 0 users
  ],
};

export default function () {

  const res = http.get(`${BASE_URL}/public/crocodiles/1/`);

  check(res, { 'status was 200': (r) => r.status == 200 });

  if(res.timings.duration > 250)
    console.log('The response is longer than expected - Response time was ' + String(res.timings.duration) + ' ms');

  sleep(1);
}
```
* Thresholds: Success/Fail criteria used to determine performance expectations
   * HTTP errors should be less than 1%
   * 95% of requests should be below 250ms

* Stages: A list of objects that specify the target number of VUs to ramp up or down
   * Up to 20 Vus (virtual users) for 2 minutes and 20 seconds over 3 stages

Inspect the k6 end-of-test summary. In the console, we can see the print of summary report to `stdout` that contains a general overview of your test results. For example:

![Load Test Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33pmw7pk3zrhsfesimwr.png)

In the example above, we can see the execution context, any logs of my script (when the response is longer than expected) and the checks result. In other words, **success** of our application.

## Bonus 🎉

### Stress Test 

> It's a type of load testing used to determine the limits of the system. The purpose of this test is to verify the stability and reliability of the system under extreme conditions.

The main idea of this test is to gradually push the application, simulating an abnormal number of operations, like a "Black Friday" and "Cyber Monday", for example. The point of this test is to gradually push your APIs beyond their breaking point.


#### Running Stress Test

For run stress test, we will use the script on folder `stress_test`:

``` shell
$ k6 run stress-test\stress-test-script.js
```

The code:

``` typescript
import http from 'k6/http';
import { sleep, check } from 'k6';

//Base Url for the test
// make sure this is not production
export const BASE_URL = 'https://test-api.k6.io';

export const options = {
  stages: [
    { duration: '2m', target: 100 }, // below normal load
    { duration: '5m', target: 100 },
    { duration: '2m', target: 200 }, // normal load
    { duration: '5m', target: 200 },
    { duration: '2m', target: 300 }, // around the breaking point
    { duration: '5m', target: 300 },
    { duration: '2m', target: 400 }, // beyond the breaking point
    { duration: '5m', target: 400 },
    { duration: '10m', target: 0 }, // scale down. Recovery stage.
  ],
};

export default function () {
  const req1 = {
    method: 'GET',
    url: `${BASE_URL}/public/crocodiles/1/`,
  };
  const req2 = {
    method: 'GET',
    url: `${BASE_URL}/public/crocodiles/2/`,
  };
  const req3 = {
    method: 'GET',
    url: `${BASE_URL}/public/crocodiles/3/`,
  };  
  const req4 = {
    method: 'GET',
    url: `${BASE_URL}/public/crocodiles/4/`,
  };  

  // call the 4 requests in parallel
  const responses = http.batch([req1, req2, req3, req4]);

  check(responses, {
    'status is 500': (r) => r.status == 500,
  });

  sleep(1);
}
```

## Conclusion

k6 makes easier the best scenarios of the load test.
For example, some benefits:
* Scripts on Javascript
* Without external dependencies
* Fast to creat test scenarios
* Easy to use version control

My opinion, it's a good alternative to Load Tests.
My next step is to create more integrations, like Grafana, Kafka, and Postman.

In this project, I used a [k6 for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=k6.k6).