API Performance Testing with k6 - A Quick Start Guide

API Performance Testing with k6 - A Quick Start Guide

posted Originally published at dev.to 7 min read

Introduction

The definition of a high-quality REST API application is an application that can operate well and perform well. The performance of the REST API can be measured by performance testing to ensure the application is operational, scalable, and reliable on many different workloads. From normal workloads up to unexpected heavy workloads.

Why Performance Testing?

The performance testing provides some benefits during REST API development:

  • Ensuring the performance of the REST API meets the desired standards, especially in unexpected workloads.

  • Detecting performance bottlenecks earlier before it went to production.

What is k6?

Many tools can be used for performance testing. One of those is k6. k6 is a performance testing tool developed by Grafana Labs for conducting performance testing in various platforms like REST API and web applications. The k6 is a code-based tool which means the testing script is written in a Javascript code. The k6 can be utilized even more by using additional plugins and extensions.

k6 Setup

These are the required steps for using k6:

  1. Install the k6. The k6 is available in Docker container and binary. Make sure to install it based on the operating system that is being used.

  2. Configure the code editor. The code editor needs to be configured to enable the IntelliSense feature to enhance the developer experience when writing testing scripts.

Writing the First Test

The k6 testing scripts can be organized into one project because when working in a project, the scripts can be organized easily.

Create the npm project by running this command.

npm init --yes

If you want to specify the additional information of the project, use npm init.

After that, install the k6 types to enable the IntelliSense feature.

npm install --save-dev @types/k6

The test script can be generated automatically using the k6 new command. This is the structure of the command.

k6 new <directory_name>/filename.js

Make sure the directory is exists.

If the directory location is not specified. This command creates a test script in the current working directory. The test script can be created manually as well.

This is the example of creating a new test script in the src directory.

k6 new src/test.js

This is the content of the created test script. The comments inside the test script is already removed.

import http from "k6/http";
import { sleep } from "k6";

export const options = {
  // A number specifying the number of VUs to run concurrently.
  vus: 10,
  // A string specifying the total duration of the test run.
  duration: "30s",
};

export default function () {
  http.get("https://test.k6.io");
  sleep(1);
}

The test can be executed using k6 run command followed by the file name.

k6 run src/test.js

This is the output of the test.

After the test is executed, the k6 generates a summary output that describes information like the number of successful requests, duration of the requests, and others.
A detailed explanation of the summary output is available here.

Load Testing Types

There are many types of load testing for measuring the performance of the system. Each type serves different purposes and requirements.

Smoke Testing

The smoke testing ensures the system is operational with a minimal load and verifies the test scripts. The smoke testing must be executed with a small number of virtual users (from 2 up to 5 VUs) and short duration.

This is an example of smoke testing for getting all posts feature.

import http from "k6/http";
import { check } from "k6";

export const options = {
  vus: 3, // 3 virtual users
  duration: "40s", // duration is 40 seconds
};

export default function () {
  // sends GET request
  const response = http.get("https://jsonplaceholder.typicode.com/posts");

  // validate the response
  check(response, {
    "is status 200": (r) => r.status === 200,
    "is not null": (r) => r.json() !== null,
  });
}

Average Load Testing

The average load testing ensures the system is operational with typical loads. Typical loads means the workload during the regular day in a production environment.

This is the example of average load testing for getting all posts feature.

import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "5m", target: 100 }, // traffic ramp-up from 1 to 100 users over 5 minutes.
    { duration: "30m", target: 100 }, // stay at 100 users for 30 minutes
    { duration: "5m", target: 0 }, // ramp-down to 0 users
  ],
};

// function for generating random string
const generateRandomString = (start, end) => {
  const res = Math.random().toString(36).substring(start, end);
  return res;
};

// function for generating random ID
const generateRandomId = () => {
  const id = Math.floor(Math.random() * 1000);
  return id;
};

export default function () {
  // prepare request body
  const requestBody = {
    title: generateRandomString(1, 7),
    body: generateRandomString(1, 20),
    userId: generateRandomId(),
  };

  // sends POST request
  const response = http.post(
    "https://jsonplaceholder.typicode.com/posts",
    JSON.stringify(requestBody),
    {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    }
  );

  // validate the response
  check(response, {
    "is status 201": (r) => r.status === 201,
    "is not null": (r) => r.json() !== null,
    "is contains valid id": (r) => r.json().id > 0,
    "is contains valid title": (r) => r.json().title !== "",
    "is contains valid body": (r) => r.json().body !== "",
  });
}

Stress Testing

The stress testing ensures the system is operational with heavier loads than usual. The stress testing verifies the stability and reliability of the system under heavy workloads. The system may receive heavy workloads in many moments such as flash-sale, payday, school or university admission process, and other similar moments. When conducting the stress testing, the number of workloads must be heavier than typical workloads and must be executed after average load testing.

This is an example of stress testing for creating a new post feature.

import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "10m", target: 200 }, // traffic ramp-up from 1 to a higher 200 users over 10 minutes.
    { duration: "30m", target: 200 }, // stay at higher 200 users for 30 minutes
    { duration: "5m", target: 0 }, // ramp-down to 0 users
  ],
};

// function for generating random string
const generateRandomString = (start, end) => {
  const res = Math.random().toString(36).substring(start, end);
  return res;
};

// function for generating random ID
const generateRandomId = () => {
  const id = Math.floor(Math.random() * 1000);
  return id;
};

export default function () {
  // prepare request body
  const requestBody = {
    title: generateRandomString(1, 7),
    body: generateRandomString(1, 20),
    userId: generateRandomId(),
  };

  // sends POST request
  const response = http.post(
    "https://jsonplaceholder.typicode.com/posts",
    JSON.stringify(requestBody),
    {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    }
  );

  // validate the response
  check(response, {
    "is status 201": (r) => r.status === 201,
    "is not null": (r) => r.json() !== null,
    "is contains valid id": (r) => r.json().id > 0,
    "is contains valid title": (r) => r.json().title !== "",
    "is contains valid body": (r) => r.json().body !== "",
  });
}

Spike Testing

The spike testing ensures the system is operational with suddenly heavy workloads. Example of sudden heavy workloads is product launch events, limited-time discount,s and others. During spike testing, the heavy workloads increased suddenly without any ramp-down or "pause time".

This is an example of spike testing for updating a post feature.

import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "2m", target: 500 }, // fast ramp-up without any break
    { duration: "1m", target: 0 }, // quick ramp-down
  ],
};

// function for generating random string
const generateRandomString = (start, end) => {
  const res = Math.random().toString(36).substring(start, end);
  return res;
};

// function for generating random ID
const generateRandomId = () => {
  const id = Math.floor(Math.random() * 100);
  return id;
};

export default function () {
  const sampleId = generateRandomId();

  // prepare request body
  const requestBody = {
    title: generateRandomString(1, 7),
    body: generateRandomString(1, 20),
    userId: sampleId,
  };

  // sends PUT request
  const response = http.put(
    `https://jsonplaceholder.typicode.com/posts/${sampleId}`,
    JSON.stringify(requestBody),
    {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    }
  );

  // validate the response
  check(response, {
    "is status 200": (r) => r.status === 200,
    "is not null": (r) => r.json() !== null,
    "is contains valid id": (r) => r.json().id > 0,
    "is contains valid title": (r) => r.json().title !== "",
    "is contains valid body": (r) => r.json().body !== "",
  });
}

Endurance Testing

The endurance testing (also known as soak testing) ensures the system is operational with average loads for a long duration. This test measures the performance degradation and the availability and stability during long periods.

The endurance testing can be executed after the smoke and average load tests are executed. The duration of this test must be longer than any other kind of test.

Actually, the typical duration is in hours like 3,4,12 ,and up to 72 hours. For this example, the duration is reduced.

This is an example of endurance testing for creating a post feature.

import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "10m", target: 100 }, // traffic ramp-up from 1 to 100 users over 10 minutes.
    { duration: "30m", target: 100 }, // stay at 100 users for 30 minutes
    { duration: "10m", target: 0 }, // ramp-down to 0 users
  ],
};

// function for generating random string
const generateRandomString = (start, end) => {
  const res = Math.random().toString(36).substring(start, end);
  return res;
};

// function for generating random ID
const generateRandomId = () => {
  const id = Math.floor(Math.random() * 1000);
  return id;
};

export default function () {
  // prepare request body
  const requestBody = {
    title: generateRandomString(1, 7),
    body: generateRandomString(1, 20),
    userId: generateRandomId(),
  };

  // sends POST request
  const response = http.post(
    "https://jsonplaceholder.typicode.com/posts",
    JSON.stringify(requestBody),
    {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    }
  );

  // validate the response
  check(response, {
    "is status 201": (r) => r.status === 201,
    "is not null": (r) => r.json() !== null,
    "is contains valid id": (r) => r.json().id > 0,
    "is contains valid title": (r) => r.json().title !== "",
    "is contains valid body": (r) => r.json().body !== "",
  });
}

Resources


I hope this article helps you to learn about API performance testing using k6.

Do you have any experience working on performance testing using k6? Please let me know in the comments down below

Thank you

If you read this far, tweet to the author to show them you care. Tweet a Thanks
Nadir: Great breakdown of performance testing with k6! Your examples are clear and well-structured. One suggestion—consider adding insights on analyzing test results and optimizing API performance based on findings. That would make this even more practical!
Nadir, have you ever encountered unexpected bottlenecks while using k6? How did you resolve them?

More Posts

API Security Testing with Damn Vulnerable API (DVAPI)

ByteHackr - Oct 14, 2024

Setting Up Next.js Authentication with Supabase: A Complete Guide

Anuj Kumar Sharma - Jan 22

Streamline WhatsApp bot development with WhatsApp API PHP SDK

Whapi Cloud API - Nov 28, 2024

Building Robust API Clients in C# with Refit

Odumosu Matthew - Jan 6

Connect a Solana wallet in Next.js to start building dApps effortlessly.

adewumi israel - Jan 24
chevron_left