How to Build Your Own Load Test: A Step-by-Step Guide

Yusuf Enes Danalıoğlu
5 min readMay 3, 2023

--

Before we build our own load test, you should know about what is load test and why we use.

Load testing is a type of performance testing that measures a system’s ability to handle a certain amount of load, such as the number of users or requests, and still maintain acceptable performance levels. Load testing is used to identify performance bottlenecks, stress-test a system under realistic conditions, and determine the maximum capacity of a system before it fails.

Load testing is particularly important for web applications, e-commerce sites, and other software systems that need to handle a high volume of traffic. It helps developers and system administrators identify and fix performance issues before they affect real users. It also provides valuable insights into a system’s performance characteristics, such as response times, throughput, and error rates, which can be used to optimize the system for better performance.

There are many load testing tools available, both open source and commercial, that can help you design and execute load tests on your systems. Some of the most popular load testing tools are:

Apache JMeter: An open-source load testing tool that can simulate a variety of load types and protocols, including HTTP, HTTPS, FTP, and JDBC.

LoadRunner: A commercial load testing tool from Micro Focus that supports a wide range of protocols and technologies, including web, mobile, and cloud applications.

Gatling: An open-source load testing tool that uses Scala to generate and execute load tests, and can simulate thousands of users with high concurrency.

Tsung: An open-source load testing tool that is designed to be distributed and can simulate millions of simultaneous users with real-time monitoring and reporting.

LoadUI: A commercial load testing tool from SmartBear that supports REST, SOAP, and other web protocols, and offers an intuitive drag-and-drop interface for designing load tests.

Choosing the right load testing tool depends on the specific needs of your project, including the type of application, the expected load, and the available resources. It’s important to carefully evaluate the features, performance, and support options of each tool before making a decision. Additionally, it’s important to properly configure and execute load tests, analyze the results, and use them to optimize your system for better performance.

Now, Let’s start building our own load test using Java.

The code block below is a load test algorithm developed with the Java programming language, where the url, requestBody, headers information of the service to be load test, the number of threads to be used for load testing and the number of calls to be made by each thread are dynamically given.

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class RestApiLoadTester {

public static void main(String[] args) {
String url = "http://localhost:8080/api/example";
String requestBody = "{\"name\": \"John\", \"age\": 30}";
String headers = "authorization-key: 0imfnc8mVLWwsAawjYr4Rx\n" +
"content-type: application/json\n" +
"user-agent: Mozilla/5.0";
int threadCount = 10;
int callCountPerThread = 100;

runLoadTest(url, requestBody, headers, threadCount, callCountPerThread);
}

public static void runLoadTest(String url, String requestBody, String headers, int threadCount, int callCountPerThread) {
OkHttpClient httpClient = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");

ExecutorService executor = Executors.newFixedThreadPool(threadCount);

for (int i = 0; i < threadCount; i++) {
executor.submit(() -> {
for (int j = 0; j < callCountPerThread; j++) {
Request.Builder requestBuilder = new Request.Builder()
.url(url);

// add headers
for (String header : headers.split("\n")) {
String[] headerParts = header.split(": ");
requestBuilder.addHeader(headerParts[0], headerParts[1]);
}

// add request body
RequestBody requestBodyObj = RequestBody.create(requestBody, mediaType);

Request request = requestBuilder
.post(requestBodyObj)
.build();

try {
Response response = httpClient.newCall(request).execute();
response.body().close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}

executor.shutdown();
}
}

Now let’s explain this algorithm

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class RestApiLoadTester {

The part where the necessary libraries (OkHttp) and classes for the Java program are imported.

    public static void main(String[] args) {
String url = "http://localhost:8080/api/example";
String requestBody = "{\"name\": \"John\", \"age\": 30}";
String headers = "authorization-key: 0imfnc8mVLWwsAawjYr4Rx\n" +
"Content-Type: application/json\n" +
"User-Agent: Mozilla/5.0";
int threadCount = 10;
int callCountPerThread = 100;

runLoadTest(url, requestBody, headers, threadCount, callCountPerThread);
}

The main() function runs at the start of the program. This function contains requests to the REST API endpoint and test parameters. The url variable contains the REST API endpoint to be tested, and the requestBody variable contains the request body in JSON format. The headers variable contains the HTTP headers appended to the request. The threadCount variable determines how many different threads will make requests, and the callCountPerThread variable determines the number of requests in each thread.

    public static void runLoadTest(String url, String requestBody, String headers, int threadCount, int callCountPerThread) {
OkHttpClient httpClient = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");

ExecutorService executor = Executors.newFixedThreadPool(threadCount);

for (int i = 0; i < threadCount; i++) {
executor.submit(() -> {
for (int j = 0; j < callCountPerThread; j++) {
Request.Builder requestBuilder = new Request.Builder()
.url(url);

// add headers
for (String header : headers.split("\n")) {
String[] headerParts = header.split(": ");
requestBuilder.addHeader(headerParts[0], headerParts[1]);
}

// add request body
RequestBody requestBodyObj = RequestBody.create(requestBody, mediaType);

Request request = requestBuilder
.post(requestBodyObj)
.build();

try {
Response response = httpClient.newCall(request).execute();
response.body().close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}

executor.shutdown();
}

We define a variable mediaType of type MediaType and assign it to the parsed value of "application/json".

We create an ExecutorService named executor that will run a fixed number of threads defined by the threadCount parameter.

We start a loop that will create a new thread for each value of i from 0 to threadCount - 1.

For each thread, we submit a lambda function that will run in the thread’s context. The function will run callCountPerThread number of HTTP POST requests to the specified URL with the specified headers and request body.

We create a new Request.Builder and set its URL to the specified url.

We loop through each header in the headers string, split it into a name and a value, and add the header to the request using the addHeader method.

We create a new RequestBody object from the specified requestBody string and mediaType.

We set the request’s method to POST and its request body to the requestBodyObj.

We execute the request using the OkHttpClient and catch any IOException that may occur.

If there is an exception, we print the stack trace.

Otherwise, we close the response body to avoid leaking resources.

Once the loop completes for all threads, we shut down the executor.

--

--

Yusuf Enes Danalıoğlu

Software Engineer @Turkish Central Bank / Interbank Card Center - Istanbul / Turkey