Introduction
In our REST Assured series, we’ve explored topics like OAuth 2.0, API Key, and Bearer Token Authentication, Digest and Form Authentication, and GPath Expressions, building a robust foundation for API testing. Now, we’ll dive into Filters, a powerful feature in REST Assured that allows you to intercept and modify HTTP requests and responses. This guide demonstrates how to use Filters to streamline logging, authentication, and custom validations, with clear examples and best practices. It’s designed for beginners and experienced developers, ensuring you can enhance your API tests efficiently.
Key Point: Filters in REST Assured enable you to customize API testing by intercepting requests and responses, allowing for reusable logic like logging, authentication, or header manipulation.
What are Filters in REST Assured?
Filters are components in REST Assured that intercept HTTP requests and responses, allowing you to perform actions such as:
- Modify Requests: Add headers, query parameters, or authentication tokens.
- Inspect Responses: Validate status codes, headers, or response bodies.
- Log Details: Capture request and response data for debugging or reporting.
- Reuse Logic: Apply common functionality (e.g., authentication) across multiple tests.
Filters implement the io.restassured.filter.Filter
interface and are applied using the filter()
method in the request specification. REST Assured provides built-in Filters (e.g., LogFilter
) and supports custom Filters for advanced use cases. We’ll use https://jsonplaceholder.typicode.com
for examples and simulate a Bearer token scenario for authentication-related Filters.
Setting Up for Filter Testing
We’ll set up a Maven project with REST Assured, JUnit, and Allure for reporting, consistent with previous posts. No additional dependencies are needed for Filters.
Here’s the pom.xml
, styled with your preferred Blogger format for XML syntax highlighting:
4.0.0
com.example
rest-assured-tests
1.0-SNAPSHOT
11
2.27.0
1.9.22
io.restassured
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
. For authentication-related Filters, we’ll use a mock Bearer token scenario.
Using Filters in REST Assured
Let’s explore how to use built-in and custom Filters to enhance API testing.
Example 1: Using Built-in LogFilter
Use the built-in LogFilter
to log request and response details.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.log.LogFilter;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Filters Testing")
public class LogFilterTest {
@Test
@Description("Test built-in LogFilter for request and response logging")
public void testLogFilter() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
given()
.filter(new LogFilter())
.when()
.get("/posts/1")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("title", notNullValue());
}
}
Explanation:
filter(new LogFilter())
: Applies theLogFilter
to log request and response details.- Logs are printed to the console, useful for debugging.
- Similar to
log().all()
but can be reused across tests.
Important: LogFilter
provides detailed logging but can generate verbose output. Use selectively in production tests.
Example 2: Custom Filter for Adding Headers
Create a custom Filter to add a common header (e.g., API key) to all requests.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.Filter;
import io.restassured.filter.FilterContext;
import io.restassured.response.Response;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.FilterableResponseSpecification;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Filters Testing")
public class CustomHeaderFilterTest {
public static class ApiKeyFilter implements Filter {
@Override
public Response filter(FilterableRequestSpecification requestSpec,
FilterableResponseSpecification responseSpec,
FilterContext ctx) {
requestSpec.header("X-Api-Key", "my-api-key");
return ctx.next(requestSpec, responseSpec);
}
}
@Test
@Description("Test custom Filter for adding API key header")
public void testApiKeyFilter() {
RestAssured.baseURI = "https://httpbin.org";
given()
.filter(new ApiKeyFilter())
.when()
.get("/headers")
.then()
.statusCode(200)
.body("headers.X-Api-Key", equalTo("my-api-key"));
}
}
Explanation:
ApiKeyFilter
: Implements theFilter
interface to add anX-Api-Key
header.requestSpec.header(...)
: Modifies the request to include the header.ctx.next(...)
: Proceeds with the request after modification.
Example 3: Custom Filter for Bearer Token Authentication
Create a custom Filter to inject a Bearer token into requests.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.Filter;
import io.restassured.filter.FilterContext;
import io.restassured.response.Response;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.FilterableResponseSpecification;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Filters Testing")
public class BearerTokenFilterTest {
public static class BearerTokenFilter implements Filter {
private final String token;
public BearerTokenFilter(String token) {
this.token = token;
}
@Override
public Response filter(FilterableRequestSpecification requestSpec,
FilterableResponseSpecification responseSpec,
FilterContext ctx) {
requestSpec.header("Authorization", "Bearer " + token);
return ctx.next(requestSpec, responseSpec);
}
}
@Test
@Description("Test custom Filter for Bearer token authentication")
public void testBearerTokenFilter() {
RestAssured.baseURI = "https://httpbin.org";
given()
.filter(new BearerTokenFilter("my-bearer-token"))
.when()
.get("/headers")
.then()
.statusCode(200)
.body("headers.Authorization", equalTo("Bearer my-bearer-token"));
}
}
Explanation:
BearerTokenFilter
: Adds aBearer
token to theAuthorization
header.- The token is passed via the Filter’s constructor, making it reusable.
- Ideal for scenarios where tokens are obtained externally (e.g., OAuth 2.0).
Pro Tip: Use Filters for authentication to centralize token management and avoid repetitive code in tests.
Example 4: Custom Filter for Response Validation
Create a custom Filter to validate response headers (e.g., Content-Type).
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.Filter;
import io.restassured.filter.FilterContext;
import io.restassured.response.Response;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.FilterableResponseSpecification;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;
@Feature("Filters Testing")
public class ResponseValidationFilterTest {
public static class ContentTypeFilter implements Filter {
@Override
public Response filter(FilterableRequestSpecification requestSpec,
FilterableResponseSpecification responseSpec,
FilterContext ctx) {
Response response = ctx.next(requestSpec, responseSpec);
assertEquals("application/json; charset=utf-8", response.header("Content-Type"),
"Unexpected Content-Type");
return response;
}
}
@Test
@Description("Test custom Filter for response header validation")
public void testContentTypeFilter() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
given()
.filter(new ContentTypeFilter())
.when()
.get("/posts/1")
.then()
.statusCode(200)
.body("id", equalTo(1));
}
}
Explanation:
ContentTypeFilter
: Validates theContent-Type
header in the response.assertEquals(...)
: Throws an assertion error if the header is incorrect.- Applied before test-specific validations, ensuring consistent checks.
Example 5: Combining Multiple Filters
Combine multiple Filters for logging, authentication, and response validation.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.log.LogFilter;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Filters Testing")
public class MultipleFiltersTest {
@Test
@Description("Test combining multiple Filters")
public void testMultipleFilters() {
RestAssured.baseURI = "https://httpbin.org";
given()
.filter(new LogFilter())
.filter(new BearerTokenFilterTest.BearerTokenFilter("my-bearer-token"))
.filter(new ResponseValidationFilterTest.ContentTypeFilter())
.when()
.get("/headers")
.then()
.statusCode(200)
.body("headers.Authorization", equalTo("Bearer my-bearer-token"));
}
}
Explanation:
- Combines
LogFilter
,BearerTokenFilter
, andContentTypeFilter
. - Filters are applied in the order specified, enabling layered functionality.
- Reduces test-specific code by centralizing common logic.
Integrating with Allure Reporting
Document Filter-based tests with Allure, attaching request and response details.
import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.filter.Filter;
import io.restassured.filter.FilterContext;
import io.restassured.response.Response;
import io.restassured.specification.FilterableRequestSpecification;
import io.restassured.specification.FilterableResponseSpecification;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Filters Testing")
public class AllureFilterTest {
public static class AllureLoggingFilter implements Filter {
@Override
public Response filter(FilterableRequestSpecification requestSpec,
FilterableResponseSpecification responseSpec,
FilterContext ctx) {
Response response = ctx.next(requestSpec, responseSpec);
Allure.addAttachment("Request URI", "text/plain", requestSpec.getURI(), ".txt");
Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
return response;
}
}
@Test
@Description("Test custom Filter with Allure reporting")
public void testFilterWithAllure() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
given()
.filter(new AllureLoggingFilter())
.when()
.get("/posts/1")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("title", notNullValue());
}
}
Explanation:
AllureLoggingFilter
: Attaches the request URI and response body to the Allure report.Allure.addAttachment
: Enhances report visibility.- Run
mvn clean test
andmvn allure:serve
to view the report.
Integrating with CI/CD
Add Filter-based tests to a GitHub Actions pipeline for automation.
Create or update .github/workflows/ci.yml
:
name: REST Assured 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 Filter-based tests with
mvn clean test
. - Publishes Allure reports to GitHub Pages for visibility.
Tips for Beginners
- Start with Built-in Filters: Use
LogFilter
to understand Filter behavior before creating custom ones. - Keep Filters Focused: Each Filter should handle a single responsibility (e.g., logging, authentication).
- Reuse Filters: Apply Filters globally using
RestAssured.filters(...)
for common logic. - Debug with Logging: Use Filters to log specific headers or parameters for troubleshooting.
Troubleshooting Tip: If a Filter fails, add logging within the Filter to inspect request/response details and verify the Filter’s logic.
What’s Next?
This post enhances our REST Assured series by introducing Filters, a versatile tool for customizing API tests. 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.