Introduction
In our REST Assured series, we’ve covered topics like Custom Assertions, POJO Mapping, and Parameterized Tests, providing a comprehensive toolkit for API testing. Now, we’ll explore Handling Timeouts, a critical aspect of testing APIs to manage slow or unresponsive endpoints. This guide demonstrates how to configure and handle timeouts in REST Assured, with practical examples and best practices. It’s designed for beginners and experienced developers, ensuring your tests remain robust and reliable.
Key Point: Handling timeouts in REST Assured ensures tests fail gracefully when APIs are slow or unresponsive, improving test reliability and debugging.
What are Timeouts in API Testing?
Timeouts define the maximum time a client (like REST Assured) waits for an API to respond before failing the request. In API testing, timeouts are crucial for:
- Reliability: Preventing tests from hanging indefinitely on slow APIs.
- Performance Validation: Ensuring APIs meet response time requirements.
- Error Handling: Capturing timeout exceptions for debugging.
- Test Efficiency: Avoiding unnecessary delays in test execution.
REST Assured allows configuration of two types of timeouts:
- Connection Timeout: Time to establish a connection to the server.
- Read Timeout: Time to receive a response after sending a request.
https://jsonplaceholder.typicode.com
for examples, testing endpoints like /posts
, and simulate timeout scenarios where applicable.
Setting Up for Timeout Handling
We’ll set up a Maven project with REST Assured, JUnit 5, and Allure for reporting, consistent with previous posts. No additional dependencies are needed for timeout configuration, as REST Assured uses its underlying HTTP client (e.g., Apache HttpClient).
Here’s the pom.xml
, styled with your preferred Blogger format for XML syntax highlighting:
4.0.0
com.example
rest-assured-timeouts
1.0-SNAPSHOT
11
2.27.0
1.9.22
io.rest-assured
rest-assured
5.4.0
test
org.junit.jupiter
junit-jupiter
5.10.2
test
org.hamcrest
hamcrest
2.2
test
com.fasterxml.jackson.core
jackson-databind
2.15.2
test
io.qameta.allure
allure-junit5
${allure.version}
test
org.apache.maven.plugins
maven-surefire-plugin
3.2.5
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
org.aspectj
aspectjweaver
${aspectj.version}
io.qameta.allure
allure-maven
2.12.0
${allure.version}
No additional setup is required for https://jsonplaceholder.typicode.com
. We’ll configure timeouts globally and per-request in the examples.
Implementing Timeout Handling
Let’s explore how to configure and handle timeouts in REST Assured, covering global settings, per-request settings, exception handling, and performance validation.
Example 1: Configuring Global Timeouts
Set global connection and read timeouts for all REST Assured requests.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.config.HttpClientConfig;
import io.restassured.config.RestAssuredConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Handling Timeouts with REST Assured")
public class GlobalTimeoutTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
// Set global timeouts: 2s connection, 3s read
RestAssured.config = RestAssuredConfig.config()
.httpClient(HttpClientConfig.httpClientConfig()
.setParam("http.connection.timeout", 2000)
.setParam("http.socket.timeout", 3000));
}
@Test
@DisplayName("Test with global timeouts")
@Description("Test API call with global connection and read timeouts")
public void testGlobalTimeouts() {
given()
.log().all()
.when()
.get("/posts/1")
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(1));
}
}
Explanation:
RestAssured.config
: Configures global timeouts usingHttpClientConfig
.http.connection.timeout
: Sets connection timeout to 2 seconds (2000 ms).http.socket.timeout
: Sets read timeout to 3 seconds (3000 ms).- Applies to all requests in the test suite.
Important: Use global timeouts for consistent behavior across tests, but override them per-request for specific endpoints if needed.
Example 2: Configuring Per-Request Timeouts
Set timeouts for a specific request.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Handling Timeouts with REST Assured")
public class PerRequestTimeoutTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@Test
@DisplayName("Test with per-request timeouts")
@Description("Test API call with per-request connection and read timeouts")
public void testPerRequestTimeouts() {
given()
.config(config().httpClient(httpClientConfig()
.setParam("http.connection.timeout", 1000)
.setParam("http.socket.timeout", 2000)))
.log().all()
.when()
.get("/posts/1")
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(1));
}
}
Explanation:
given().config
: Applies timeouts only to the current request.- Connection timeout: 1 second (1000 ms); read timeout: 2 seconds (2000 ms).
- Useful for endpoints with specific performance requirements.
Example 3: Handling Timeout Exceptions
Catch and handle timeout exceptions using JUnit.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.net.SocketTimeoutException;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;
@Feature("Handling Timeouts with REST Assured")
public class TimeoutExceptionTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://httpbin.org";
}
@Test
@DisplayName("Test handling timeout exception")
@Description("Test catching a timeout exception for a slow API")
public void testTimeoutException() {
// Use httpbin.org/delay/5 to simulate a slow response (5s delay)
Exception exception = assertThrows(Exception.class, () -> {
given()
.config(config().httpClient(httpClientConfig()
.setParam("http.connection.timeout", 1000)
.setParam("http.socket.timeout", 1000)))
.log().all()
.when()
.get("/delay/5")
.then()
.log().all()
.statusCode(200);
});
assertTrue(exception.getCause() instanceof SocketTimeoutException,
"Exception should be a SocketTimeoutException");
}
}
Explanation:
httpbin.org/delay/5
: Simulates a 5-second delay, exceeding the 1-second read timeout.assertThrows
: Captures the timeout exception (typicallySocketTimeoutException
).- Validates the exception type for accurate error handling.
Pro Tip: Always catch timeout exceptions in tests to provide meaningful failure messages and avoid test hangs.
Example 4: Validating Response Time
Validate that an API responds within an acceptable time.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;
@Feature("Handling Timeouts with REST Assured")
public class ResponseTimeTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@Test
@DisplayName("Test validating response time")
@Description("Test ensuring API responds within acceptable time")
public void testResponseTime() {
long maxResponseTime = 2000; // 2 seconds
Response response = given()
.log().all()
.when()
.get("/posts/1")
.then()
.log().all()
.statusCode(200)
.extract().response();
long responseTime = response.getTime();
assertTrue(responseTime <= maxResponseTime,
"Response time " + responseTime + "ms exceeds " + maxResponseTime + "ms");
}
}
Explanation:
response.getTime()
: Returns the response time in milliseconds.- Asserts the response time is within 2 seconds.
- Useful for performance testing or SLA validation.
Example 5: Timeout Handling with Allure
Integrate timeout handling with Allure reporting.
import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.net.SocketTimeoutException;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;
@Feature("Handling Timeouts with REST Assured")
public class AllureTimeoutTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://httpbin.org";
}
@Test
@DisplayName("Test timeout with Allure reporting")
@Description("Test handling timeout with Allure reporting")
public void testTimeoutWithAllure() {
try {
given()
.config(config().httpClient(httpClientConfig()
.setParam("http.connection.timeout", 1000)
.setParam("http.socket.timeout", 1000)))
.log().all()
.when()
.get("/delay/5")
.then()
.log().all()
.statusCode(200);
fail("Expected timeout exception");
} catch (Exception e) {
if (e.getCause() instanceof SocketTimeoutException) {
Allure.addAttachment("Timeout Error", "text/plain",
"Timeout occurred: " + e.getMessage(), ".txt");
} else {
throw e;
}
}
}
}
Explanation:
- Catches
SocketTimeoutException
and attaches error details to Allure. Allure.addAttachment
: Logs the timeout error for debugging.- Run
mvn clean test
andmvn allure:serve
to view the report.
Integrating with CI/CD
Add timeout handling tests to a GitHub Actions pipeline for automation.
Create or update .github/workflows/ci.yml
:
name: REST Assured Timeouts CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
- name: Run tests
run: mvn clean test
- name: Generate Allure report
run: mvn allure:report
- name: Upload Allure results
if: always()
uses: actions/upload-artifact@v4
with:
name: allure-results
path: target/allure-results
- name: Publish Allure report
if: always()
uses: simple-elf/allure-report-action@v1.7
with:
allure_results: target/allure-results
gh_pages: gh-pages
allure_report: allure-report
- name: Deploy report to GitHub Pages
if: always()
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: allure-report
Explanation:
- Runs timeout handling tests with
mvn clean test
. - Publishes Allure reports to GitHub Pages for visibility.
Tips for Beginners
- Set Reasonable Timeouts: Choose timeouts based on API SLAs or typical response times.
- Use Per-Request Timeouts Sparingly: Prefer global timeouts for consistency unless specific endpoints require different settings.
- Log Timeouts: Enable
log().all()
to capture request details for failed timeout tests. - Monitor Performance: Regularly validate response times to detect performance degradation.
Troubleshooting Tip: If a timeout occurs unexpectedly, check network conditions, increase the timeout temporarily for debugging, and use log().all()
to inspect the request.
What’s Next?
This post enhances our REST Assured series by introducing Handling Timeouts, a key technique for reliable API testing. To continue your learning, consider exploring:
- GraphQL Testing: Testing GraphQL APIs with REST Assured.
- Advanced Allure Reporting: Customizing reports with environments and categories.
- End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.