Unleash the Power: Getting Started with Load Testing using k6
Unleash the Power: Getting Started with Load Testing using k6
Is your application ready to handle the rush? Whether you're launching a new feature, anticipating a marketing campaign, or simply want to ensure a robust user experience, understanding your system's performance under load is crucial. This is where load testing comes in, and k6 is a fantastic, developer-friendly tool to help you do just that.
What is k6?
k6 is an open-source load testing tool designed for developers. It allows you to write your tests in JavaScript (ES2015/ES6), making it accessible and easy to integrate into your existing development workflows. Key benefits include:
- Developer-Friendly: Write tests in JavaScript, a language many developers are already comfortable with.
- Performance: Written in Go, k6 is highly performant and can generate significant load from a single machine.
- Goal-Oriented: Define pass/fail criteria (Thresholds) directly in your scripts.
- Extensible: Supports various protocols (HTTP/1.1, HTTP/2, WebSockets, gRPC) and can be extended with custom JavaScript modules or Go extensions.
- CI/CD Integration: Easily integrate k6 into your continuous integration and delivery pipelines.
Before We Dive In: Installation
Getting k6 installed is straightforward. You can download a pre-built binary, use a package manager, or even run it via Docker.
For most systems, you can follow the official installation guide:
k6 Installation GuideOnce installed, verify it by typing k6 version
in your terminal.
Your First k6 Script: A Simple GET Request
Let's write our first k6 script. Create a file named script.js
and add the following content:
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
// Make a GET request to a test API
let response = http.get('https://test-api.k6.io/public/crocodiles/');
// Optional: log the response status
console.log(`Response status: ${response.status}`);
// Think time: pause for 1 second between iterations
sleep(1);
}
Let's break this down:
import http from 'k6/http';
: Imports the k6 HTTP module, which allows us to make HTTP requests.import { sleep } from 'k6';
: Imports thesleep
function to pause execution.export default function () { ... }
: This is the main function that k6 will execute. Each execution of this function is called an "iteration" and is typically performed by a "Virtual User" (VU).http.get('https://test-api.k6.io/public/crocodiles/');
: Sends a GET request to the specified URL. k6 provides a public test API you can use for experimentation.console.log(...)
: (Optional) Logs information to the console. Be mindful that excessive logging can impact performance during large tests.sleep(1);
: Pauses the VU for 1 second. This simulates user "think time" and prevents overwhelming the target system too quickly.
Running Your Script
To run this script, open your terminal, navigate to the directory where you saved script.js
, and execute:
k6 run script.js
By default, k6 will run this script with 1 Virtual User (VU) for 1 iteration.
Understanding the Output
After the script finishes, k6 will print a summary of the results. You'll see various metrics, including:
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: script.js
output: -
scenarios: (100.00%) 1 scenario, 1 max VUs, 10s max duration (incl. graceful stop):
* default: 1 iteration for 1 VUs (maxDuration: 10s, gracefulStop: 30s)
INFO[0001] Response status: 200 source=console
✓ status was 200
checks.........................: 100.00% ✓ 1 ✗ 0
data_received..................: 1.3 kB 1.1 kB/s
data_sent......................: 81 B 66 B/s
http_req_blocked...............: avg=71.13ms min=71.13ms med=71.13ms max=71.13ms p(90)=71.13ms p(95)=71.13ms
http_req_connecting............: avg=31.1ms min=31.1ms med=31.1ms max=31.1ms p(90)=31.1ms p(95)=31.1ms
http_req_duration..............: avg=238.27ms min=238.27ms med=238.27ms max=238.27ms p(90)=238.27ms p(95)=238.27ms
{ expected_response:true }...: avg=238.27ms min=238.27ms med=238.27ms max=238.27ms p(90)=238.27ms p(95)=238.27ms
http_req_failed................: 0.00% ✓ 0 ✗ 0
http_req_receiving.............: avg=108.2µs min=108.2µs med=108.2µs max=108.2µs p(90)=108.2µs p(95)=108.2µs
http_req_sending...............: avg=41.3µs min=41.3µs med=41.3µs max=41.3µs p(90)=41.3µs p(95)=41.3µs
http_req_tls_handshaking.......: avg=38.18ms min=38.18ms med=38.18ms max=38.18ms p(90)=38.18ms p(95)=38.18ms
http_req_waiting...............: avg=238.12ms min=238.12ms med=238.12ms max=238.12ms p(90)=238.12ms p(95)=238.12ms
http_reqs......................: 1 0.818658/s
iteration_duration.............: avg=1s min=1s med=1s max=1s p(90)=1s p(95)=1s
iterations.....................: 1 0.818658/s
vus............................: 1 min=1 max=1
vus_max........................: 1 min=1 max=1
Key metrics to look at initially:
vus
: The number of active virtual users.iterations
: The total number of times the default function was executed.http_req_duration
: The time it took for requests to complete (excluding time spent waiting in queue). Look at `avg`, `med` (median), `p(90)`, `p(95)` (90th and 95th percentiles).http_req_failed
: The percentage of failed requests. Ideally, this should be 0%.checks
: (We'll cover these next) The success rate of your custom assertions.
Adding Some Load: VUs and Duration
Running with 1 VU for 1 iteration isn't much of a "load" test. Let's configure k6 to run with more VUs over a specific duration. We do this using the options
object.
Modify your script.js
:
import http from 'k6/http';
import { sleep } from 'k6';
// Configure test options
export const options = {
vus: 10, // Simulate 10 virtual users
duration: '30s', // Run the test for 30 seconds
};
export default function () {
http.get('https://test-api.k6.io/public/crocodiles/');
sleep(1);
}
Now, when you run k6 run script.js
, k6 will ramp up to 10 VUs and continuously execute the default function for 30 seconds.
Making Assertions: Checks
A load test isn't just about sending traffic; it's about verifying that your system behaves correctly under load. k6 uses "Checks" for this. Checks are like assertions but don't halt the test execution if they fail.
Update script.js
:
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
const res = http.get('https://test-api.k6.io/public/crocodiles/1/'); // Get a specific crocodile
// Perform checks on the response
check(res, {
'status is 200': (r) => r.status === 200,
'response body contains name': (r) => r.body.includes('Bert'), // Assuming Bert is a known crocodile
});
sleep(1);
}
Now, the output will include a checks
section showing the success rate of your assertions.
Defining Success: Thresholds
Thresholds allow you to define pass/fail criteria for your load test. If any threshold isn't met, k6 will exit with a non-zero exit code, which is useful for CI/CD pipelines.
Add thresholds to your options
:
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 10,
duration: '30s',
thresholds: {
// 95% of requests must complete within 200ms
'http_req_duration': ['p(95)<200'],
// Less than 1% of requests should fail
'http_req_failed': ['rate<0.01'],
// At least 99% of checks should pass
'checks': ['rate>0.99'],
},
};
export default function () {
const res = http.get('https://test-api.k6.io/public/crocodiles/1/');
check(res, {
'status is 200': (r) => r.status === 200,
'response body contains name': (r) => r.body.includes('Bert'),
});
sleep(1);
}
If any of these thresholds are breached, k6 will indicate this in the summary.
Why k6 Shines
- Simplicity and Power: It strikes a great balance between ease of use for beginners and powerful features for advanced scenarios.
- Performance: Being written in Go, it's incredibly efficient at generating load.
- Scripting in JavaScript: Lowers the barrier to entry for many developers and testers.
- Rich Ecosystem: Integrates with Grafana (for visualization), Prometheus, and various CI/CD tools. It also supports outputting results in different formats like JSON or CSV.
Next Steps
This was just a primer to get you started! From here, you can explore:
- Scenarios: More complex load patterns with different VU profiles.
- Modules: Organize your code and share common logic.
- Data Parameterization: Using external CSV files for test data.
- Cloud Execution: Running distributed tests with k6 Cloud.
- Different Protocols: Testing WebSockets, gRPC, etc.
The official k6 documentation is an excellent resource for diving deeper.
Explore k6 DocumentationHappy testing! Understanding your application's limits is the first step to building more resilient and performant systems.
Comments