Testing Spring Web MVC Filter with Spring Boot

Last Updated:  April 14, 2025 | Published: April 12, 2025

When developing Spring Boot web applications, filters are essential for implementing cross-cutting concerns like rate limiting, authentication, or logging.

While it’s common to test filters with plain unit tests and mocked dependencies, this approach often fails to capture how filters interact with Spring’s request processing pipeline.

This article explores a better way to test Spring MVC filters using the @WebMvcTest annotation, providing more realistic and comprehensive validation.

Understanding Spring MVC Filters

Before diving into testing approaches, let’s understand what we’re testing. A Spring MVC filter intercepts HTTP requests before they reach your controllers or responses before they return to the client. Here’s a simple rate-limiting filter that we’ll use throughout this article:

This filter maintains a count of requests from each client IP address and rejects requests that exceed a configurable limit. The filter extends Spring’s OncePerRequestFilter, which ensures it’s applied only once per request, even in complex request dispatching scenarios.

The logic is straightforward:

  1. Extract the client’s IP address
  2. Increment the request count for that IP
  3. If the count exceeds the limit, return a 429 (Too Many Requests) status
  4. Otherwise, continue the filter chain

The Limitations of Plain Unit Tests for Filters

A traditional approach to testing this filter would use unit tests with Mockito to mock the request, response, and filter chain. Let’s look at how that would work:

First, we set up the test class with mocked dependencies for the filter’s inputs. Next, we initialize the filter and configure basic mock behavior:

Now we can write tests for the filter’s behavior:

While this test validates the filter’s core logic, it has several limitations:

  1. Artificial environment: The test runs outside Spring’s context, so we don’t test how the filter is registered or ordered in the filter chain.
  2. Limited validation: We’re only verifying method calls on mocked objects, not the actual HTTP response behavior.
  3. Missing context: We can’t easily test how the filter interacts with actual controllers or other Spring components.
  4. Complex mock setup: For more sophisticated filters, setting up all the necessary mocks can be tedious and error-prone.

A Better Approach with @WebMvcTest

The @WebMvcTest annotation creates a sliced Spring application context focused on the web layer.

This allows us to test our filter in a more realistic environment that includes Spring’s MVC infrastructure.

Before we test the filter, we define a simple test controller that we’ll use to verify the filter’s behavior. This controller has a single endpoint that returns a success message.

Let’s see how to test the filter using @WebMvcTest:

First, we set up a test class with the @WebMvcTest annotation. This creates a Spring application context with just the web components.

The MockMvc bean allows us to simulate HTTP requests to our application.ow we can write tests that use MockMvc to send HTTP requests through our filter to the controller:

This test verifies that the first two requests are allowed through the filter, reaching the controller and returning a 200 OK status with the expected content.

Next, we test the rate-limiting behavior:

This test verifies that after two successful requests, the third request is blocked by the filter, returning a 429 Too Many Requests status with the appropriate error message.

The @WebMvcTest approach has several advantages:

  1. Realistic environment: The test runs within Spring’s context, testing how the filter integrates with Spring’s request processing pipeline.
  2. Complete validation: We can verify the entire HTTP response, including status code, headers, and content.
  3. Actual integration: We test the filter’s interaction with a real controller and Spring’s MVC infrastructure.
  4. Simplified setup: Spring handles the wiring and HTTP processing, so we don’t need complex mock configurations

Practical Testing Tips and Best Practices

When testing Spring MVC filters with @WebMvcTest, consider these best practices:

  1. Isolate Test Scenarios: Configure each test class to focus on a specific filter or interaction pattern.
  2. Use @Import Selectively: Only import the filters and components necessary for your test to keep the context lightweight.
  3. Testing Logging: For filters that log information, consider:
    • Using a test appender with SLF4J
    • Creating a testable wrapper around your filter that exposes what was logged
    • Using a framework like Log4j’s ListAppender for capturing log events
  4. Test Filter Chain: Ensure your filter correctly calls chain.doFilter() and handles the filter chain properly.
  5. Real HTTP Requests: For more comprehensive testing, consider using @SpringBootTest with TestRestTemplate or WebTestClient to make real HTTP requests through your filter chain.
  6. Filter Registration Testing: If you’re registering filters programmatically using FilterRegistrationBean, include tests for the registration logic as well:

Summary

Testing Spring MVC filters effectively requires going beyond simple unit tests to validate their integration with Spring’s request processing pipeline.

By using Spring Boot’s @WebMvcTest annotation, we can test filters within a realistic Spring context, ensuring they function correctly with controllers and other components.

Key takeaways from this article:

  1. Unit testing filters in isolation misses important integration aspects with Spring MVC
  2. @WebMvcTest provides a more comprehensive testing approach for filters
  3. Use @Import to include your filters in the test Spring context
  4. Test multiple filters together to validate their ordering and interactions
  5. Consider both happy paths and exception scenarios in your filter tests

With these techniques, we can develop robust, well-tested filters that reliably perform their cross-cutting concerns in our Spring Boot applications, from authentication to logging, rate limiting, and beyond. Remember that thorough filter testing contributes significantly to the overall stability and reliability of your application’s web layer.

Joyful testing,

Philip

>