Hello Folks, Performance testing is an important aspect of software development, helping testers ensure that their applications can handle heavy loads and maintain performance under stress.K6 is an open-source load testing tool that is widely used for performance testing and provides testers with an easy-to-use interface for writing performance tests. However, writing efficient K6 scripts can be challenging. In this blog post, we will explore some of the best practices for writing efficient K6 scripts for performance testing.
Defining Your Objectives
Before you begin writing K6 scripts, it’s important to define your testing objectives. What are you trying to test? What kind of performance metrics are you interested in? Which load and stress scenarios do you really want to simulate? Defining these objectives upfront will help you write more focused and efficient K6 scripts. The objectives could be to test the website’s response time, throughput, or capacity. It is important to define these objectives before starting the performance testing, as it helps in creating a focused test plan.
- Identify the purpose of the test
- Identify the performance metrics that you want to measure during the test. Some common performance metrics include response time, throughput, error rate, and concurrent user load. It’s important to establish a baseline for these metrics so that you can accurately evaluate the performance of your application.
- Decide on the workload profile that you want to test. This includes factors like the number of virtual users, the duration of the test, and the types of requests that you are going to send.
- Establish your acceptance criteria: Determine the performance thresholds that your application needs to meet in order to be considered acceptable. For example, you may decide that your application needs to maintain a response time of under 500ms for 90% of requests.
Using Virtual Users in K6 scripts
K6 allows simulating virtual users to test the application or website’s performance. Virtual users are simulated users that mimic the behaviour of real users. Instead of testing your application with a single user, you should use virtual users to simulate multiple users and determine how your application performs under different loads. Using virtual users can help you uncover performance issues that you may not have detected otherwise.
To use virtual users in a K6 script, you can define a function that represents a single user’s behaviour and then use the K6 function API to create multiple virtual users running that function simultaneously.
Here is an example script that creates 10 virtual users, each running a function that makes an HTTP GET request to a URL:
import http from "k6/http";
import { check } from "k6";
export let options = {
vus: 10, //You can keep virtual users as per your test requirement//
duration: "30s" //Time to inject above virtual users//
};
export default function() {
let res = http.get('https://reqres.in/api/users/2');
check(res, {
"status is 200": (r) => r.status === 200
});
}
In this script, we have defined an options object that specifies the number of virtual users (vus) to create and the duration of the test (duration). We then define a default function that represents the behaviour of each virtual user. This function makes an HTTP GET request to a URL and checks that the response status is 200.
When we run this script using the k6 run command, K6 will create 10 virtual users, each running the default function, and execute the test for 30 seconds.
Using Environment Variables in K6 scripts
Using environment variables is a best practice for writing efficient K6 scripts. Environment variables can help you manage dynamic values such as usernames, passwords, and endpoints. It is a good practice to use environment variables to store sensitive information, as it helps in keeping the sensitive information secure. By using environment variables, you can easily switch between test environments and ensure that your scripts are running with the correct values.
- Set environment variables:
You can set environment variables either by using your command-line interface or by using a .env file.
For command-line interface$ export MY_VAR="my_value"
For .env file
Create a .env file in the same directory as your script file with the below syntax:MY_VAR=my_value
- Use environment variables in your script:
You can use environment variables in your script by accessing them with the __ENV global object.import http from "k6/http";
export default function () {
const response = http.get(${__ENV.MY_VAR}/users);
console.log(response.body);
}
In this example, we are using the __ENV global object to access the value of the MY_VAR environment variable.
Using Iterations in K6 scripts
K6 offers a range of iteration features that can help you simulate different load and stress scenarios. Using iterations can help you replicate real-world usage patterns and determine how your application performs under different conditions. To use iterations in a K6 script, you can use the built-in iteration functions provided by K6, such as for loops or forEach loops.
Here’s an example of a simple for loop in a K6 script that iterates 5 times:
import http from 'k6/http';
import { sleep } from 'k6';
export default function() {
for (let i = 0; i < 5; i++) {
http.get('https://reqres.in/api/users/2');
sleep(1);
}
}
In this example, the for loop iterates 5 times, making an HTTP GET request to https://reqres.in/ and then sleeping for 1 second before iterating again.
You can also use the forEach function in K6 to iterate over an array of data. Here’s an example:
import http from 'k6/http';
import { sleep } from 'k6';
export default function() {
const urls = [
'https://reqres.in/api/users/2',
'https://reqres.in/api/users?page=2',
'https://reqres.in/api/unknown'
];
urls.forEach(function(url) {
http.get(url);
sleep(1);
});
}
In this example, the forEach function iterates over an array of URLs, making an HTTP GET request to each URL and then sleeping for 1 second before moving on to the next URL.
Using Thresholds in K6 scripts
Thresholds are another important feature of K6 that can help you define performance objectives and validate your test results. Thresholds enable you to set specific performance targets for your application and ensure that your application meets those targets. It is a good practice to set test result thresholds, as it helps in identifying the performance bottlenecks that we should fix before deploying the application or website to production. In a K6 script, you can use thresholds to set performance thresholds for specific metrics. These thresholds allow you to define acceptable levels of performance for different aspects of your load test, such as response times, error rates, and throughput.
Here’s an example of how to use thresholds in a K6 script:
import http from "k6/http";
import { check, sleep } from "k6";
export let options = {
thresholds: {
http_req_duration: ["p(95)<500"], // 95% of requests must complete within 500ms //
http_req_failed: ["rate<0.01"], // Error rate must be less than 1% //
http_reqs: ["rate>100"], // Must generate at least 100 requests per second //
},
};
export default function () {
let res = http.get('https://reqres.in/api/users/2');
check(res, {
"status is 200": (r) => r.status === 200,
});
sleep(1);
}
In this example, we’ve set three thresholds using the thresholds option in the options object. The first threshold (http_req_duration) specifies that 95% of requests must be completed within 500ms. The second threshold (http_req_failed) specifies that the error rate must be less than 1%. The third threshold (http_reqs) specifies that we must generate at least 100 requests per second.
Inside the default function, we make an HTTP request to https://reqres.in/api/users/2, and then use the check function to verify that the response status is 200. We also include a sleep statement to introduce a 1-second delay between requests.
During the load test, K6 will track the performance of each threshold and display the results in the console output. If any of the thresholds are not met, K6 will generate a warning or error message depending on the severity of the issue.
Using Assertions in K6 scripts
Assertions are a key feature of K6 that can help you validate your test results. Assertions enable you to define specific performance metrics that you want to monitor, such as response time, throughput, and error rate. By using assertions, you can ensure that your application is meeting your performance objectives. You may refer to the documentation as well.
In K6 scripts, you can use the built-in assert function to check certain conditions and make sure they are true during the load test. Here’s an example:
import http from 'k6/http';
import { sleep } from 'k6';
export default function() {
const response = http.get('https://reqres.in/api/users/2');
sleep(1);
assert(response.status === 200, 'Response status code is not 200');
assert(response.headers['Content-Type'] === 'text/html', 'Response content type is not text/html');
}
In the above example, we’re making a GET request to https://reqres.in/, and then using assert to check that the response status code is 200 and the content type is text/html. If either of these conditions is not true, the load test will fail with an error message.
Using CSV Files for Test Data in K6 scripts
K6 allows the use of CSV files for test data. It is a good practice to use CSV files for test data, as it helps in creating large amounts of test data quickly. Using CSV files also makes it easy to modify the test data without modifying the K6 script.
To get data from a CSV file in a K6 script, you can use the k6/x/encoding/CSV module, which provides a parse() function that allows you to parse a CSV file and convert it into an array of objects.
Here’s an example of how you can use this module to get data from a CSV file:
import { parse } from 'k6/x/encoding/csv';
import { open } from 'k6';
export function setup() {
// Open the CSV file //
const file = open('data.csv');
// Parse the CSV file and return the data //
return parse(file);
}
export default function(data) {
// Use the data in your load test //
}
In this example, the setup() function is called before the load test starts and opens the CSV file using the open() function. Then, it uses the parse() function to parse the CSV file and return the data as an array of objects.
The default function is the main function of the load test and is called repeatedly during the test. It takes the data returned by the setup() function as an argument and can use it to generate load.
Note that the parse() function assumes that the first row of the CSV file contains the headers for the columns. If your CSV file doesn’t have headers, you can pass an options object as the second argument to the parse() function to specify the headers manually.
import { parse } from 'k6/x/encoding/csv';
import { open } from 'k6';
export function setup() {
// Open the CSV file //
const file = open('data.csv');
// Parse the CSV file and return the data //
return parse(file, { headers: ['column1', 'column2', 'column3'] });
}
export default function(data) {
// Use the data in your load test //
}
In this example, the parse() function is passed an options object with a headers property that specifies the headers for the columns.
Conclusion
In conclusion, writing efficient K6 scripts for performance testing requires careful planning and attention to detail. By following these best practices, you can ensure that your K6 scripts are focused, efficient, and effective at identifying performance issues in your application.