Introduction
In modern web development, applications increasingly need to handle high concurrency while maintaining low latency and responsiveness. This is where the reactive programming paradigm shines. With reactive programming, developers can efficiently manage asynchronous, non-blocking operations, enabling better resource utilization and responsiveness under heavy load.
The Spring WebFlux project brings the reactive programming model to the Spring ecosystem. Spring WebFlux is designed to manage many concurrent connections efficiently, making it ideal for building scalable and resilient applications.
With Diffblue Cover’s release in April 2024, support has been added for testing Spring WebFlux applications. Diffblue Cover can now automatically write unit tests for the reactive Spring stack following Spring testing best practices.
In this post, we’ll explore what makes Spring WebFlux different from traditional code, how it can transform how we build and test applications, and how Diffblue Cover can make writing tests for reactive applications effortless and reliable.
Understanding Spring WebFlux
Spring WebFlux is a reactive, non-blocking framework introduced in Spring 5. Built on top of Project Reactor, WebFlux operates with a reactive programming model designed for applications that need to handle large volumes of requests without slowing down server resources.
Traditional blocking I/O approaches typically process requests sequentially, holding up resources until each request is completed. In contrast, WebFlux operates with a non-blocking I/O model, which allows the server to process multiple requests simultaneously without tying up threads, making it an ideal choice for highly scalable applications.
Key Features of Spring WebFlux
- Non-Blocking I/O Model: By leveraging a non-blocking I/O model, WebFlux efficiently manages concurrent requests, allowing it to handle a large number of clients without thread saturation.
- Enhanced Scalability: Ideal for resource-intensive applications, WebFlux’s scalability makes it suitable for applications that need to serve multiple requests concurrently without increasing memory overhead.
- Responsive and Resilient: With WebFlux, applications are more responsive under heavy load, ensuring a smoother user experience.
Real-World Use Cases for Spring WebFlux
Spring WebFlux shines in scenarios that require handling multiple simultaneous connections.
Examples include:
- Real-Time Applications: Applications that need live data updates, such as chat applications or live sports scores.
- IoT Data Streams: Systems that collect data from multiple sensors or IoT devices, where each device has intermittent connectivity.
- High-load Microservices: Non-blocking WebFlux applications can function without overloading server resources, enhancing response times in microservice environments.
Writing Unit Tests for Spring WebFlux with Diffblue
With its new support for Spring WebFlux, Diffblue Cover extends its existing capabilities to autonomously generate unit tests at scale specifically for reactive Spring WebFlux code.
Traditionally, Diffblue Cover has helped Java developers save time and effort using reinforcement learning to generate unit tests for conventional synchronous Java applications. Now, with Spring WebFlux support, Diffblue Cover makes it easier to automate the testing of Mono and Flux return types, which are key to reactive programming in Spring. By automatically generating unit tests for these reactive components, Diffblue Cover ensures that WebFlux applications achieve comprehensive test coverage, even across complex, asynchronous code paths.
Comprehensive Coverage and High Code Quality
The automation provided by Diffblue Cover doesn’t just save time—it enhances the depth and accuracy of test coverage in WebFlux applications. Diffblue’s AI generates tests that cover multiple scenarios and edge cases, helping developers achieve a higher level of code quality without the manual effort traditionally required.
Reactive applications often contain intricate, asynchronous flows, which can be challenging to cover manually. Diffblue Cover’s WebFlux support helps identify these flows, ensuring that test suites address every corner of the reactive application.
Improved Efficiency and Accuracy
The time and resources saved by using Diffblue Cover in WebFlux projects translate to significant productivity gains for developers. Instead of spending hours or even days manually writing and maintaining unit tests, development teams can rely on Diffblue Cover’s unit test suite. By automating test creation, Diffblue Cover helps eliminate human error, producing tests that are consistent, reliable, and aligned with the latest changes in code. For developers, this means faster iterations, more accurate testing, and the confidence that WebFlux components are thoroughly validated.
💡 How it works: Diffblue analyzes your compiled bytecode, explores runtime behavior, and generates tests that reflect real execution paths. The result: fast, readable, runnable unit tests with high coverage.
📦 On-Premise and CI Friendly: Cover runs entirely in your environmen, —no source code leaves your firewall. Use the IntelliJ plugin or CLI locally, or embed it in your CI pipelines.
Best Practices for Unit Testing Spring WebFlux
Testing reactive code requires different thinking from traditional synchronous Java. Spring WebFlux encourages asynchronous, non-blocking flows, which adds complexity to writing and verifying unit tests. Fortunately, Spring offers dedicated testing tools, and Diffblue Cover now supports these patterns out of the box.
Here’s how to approach unit testing in WebFlux effectively, and how Diffblue can help:
Reactive Testing Support
Spring WebFlux includes built-in support for testing reactive components. When testing HTTP controllers, WebFlux provides the WebTestClient
. This client makes it easy to send mock HTTP requests to reactive endpoints, allowing developers to validate the status and headers of responses and the content of reactive streams, such as Flux
and Mono
.
The WebTestClient
is a powerful tool for testing Spring WebFlux applications because it can operate both with a fully configured application context and in a standalone mode, making it versatile for different testing scenarios.
Let’s see how Diffblue Cover generates the unit test(s) for a sample Spring WebFlux controller endpoint, freeing us from time in our development workflow.
Our GreetingController
returns a reactive Mono
stream with a greeting message:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api")
public class GreetingController {
@GetMapping("/greet")
public Mono<String> greet() {
return Mono.just("Hello, welcome to Spring WebFlux!");
}
}
We’re going to use the Diffblue Cover Plugin for IntelliJ IDEA to generate the tests, but the same result can be achieved with the CLI or Pipeline version of Diffblue Cover.
As we’re anyway developing inside our IDE, this workflow comes naturally, and with the click of a button, we start Diffblue’s test generation process:

Right after Diffblue Cover finishes the test generation, we get a success message in the Diffblue Cover Plugin information panel that our tests are ready:

When navigating to the test’s location, we find the following autonomously generated unit test:
@WebFluxTest(controllers = {GreetingController.class})
@ContextConfiguration(classes = {GreetingController.class})
@ExtendWith(SpringExtension.class)
class GreetingControllerDiffblueTest {
@Autowired
private GreetingController greetingController;
@Autowired
private WebTestClient webTestClient;
/**
* Test {@link GreetingController#greet()}.
* <p>
* Method under test: {@link GreetingController#greet()}
*/
@Test
@DisplayName("Test greet()")
void testGreet() throws AssertionError {
// Arrange and Act
WebTestClient.ResponseSpec actualExchangeResult = ((WebTestClient.RequestHeadersSpec<?>) webTestClient.get()
.uri("/api/greet")).exchange();
// Assert
actualExchangeResult.expectStatus().isOk().expectHeader().contentType("text/plain;charset=UTF-8");
Class<String> elementClass = String.class;
StepVerifier.FirstStep<String> createResult = StepVerifier
.create(actualExchangeResult.returnResult(elementClass).getResponseBody());
createResult.assertNext(s -> {
assertEquals("Hello, welcome to Spring WebFlux!", s);
return;
}).expectComplete().verify();
}
}
Diffblue Cover made use of the sliced context annotation @WebFluxTest
to verify our controller in isolation.
The actual test uses Spring testing best practices and the already mentioned WebTestClient
to simulate a mock HTTP request against our controller.
What follows are assertions for the content type of our response and making sure the correct string message is returned.
Furthermore, Diffblue Cover structures the test in the arrange/act/assert structure so that we can easily identify how the test is built. This improves the maintainability and readability of our tests over time.
Isolated Testing with the Help of Mocked Components
Spring WebFlux promotes a modular and decoupled architecture, making it easier to write isolated unit tests for individual components. We can mock dependencies using tools like Mockito, allowing us to test components in isolation without relying on external resources like databases or external services. By mocking dependencies, developers can simulate different scenarios and behaviors, ensuring that each component functions correctly under various conditions.
To illustrate this, let’s consider a scenario where we extend our GreetingController’s implementation by a new endpoint that uses a service to fetch data from an external resource:
@RestController
@RequestMapping("/api")
public class GreetingController {
private final GreetingService greetingService;
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/greetings")
public Flux<String> greetings(@RequestParam("lang") String language) {
return greetingService.fetchByLanguage(language);
}
}
The new /greetings
endpoint uses the GreetingService
to fetch all available greetings for a given language. The service itself may use an external service or make a database query to get the information.
When verifying our controller as a unit, we avoid the internals of any dependent class by mocking them.
Let’s see what Diffblue Cover generates for us for this new method:
@DisabledInAotMode
@WebFluxTest(controllers = {GreetingController.class})
@ContextConfiguration(classes = {GreetingController.class})
@ExtendWith(SpringExtension.class)
class GreetingControllerDiffblueTest {
@MockBean
private GreetingService greetingService;
@Autowired
private GreetingController greetingController;
@Autowired
private WebTestClient webTestClient;
/**
* Test {@link GreetingController#greetings(String)}.
* <p>
* Method under test: {@link GreetingController#greetings(String)}
*/
@Test
@DisplayName("Test greetings(String)")
void testGreetings() throws AssertionError {
// Arrange
Flux<String> fromIterableResult = Flux.fromIterable(new ArrayList<>());
when(greetingService.fetchByLanguage(Mockito.<String>any())).thenReturn(fromIterableResult);
WebTestClient.RequestHeadersUriSpec<?> getResult = webTestClient.get();
// Act
WebTestClient.ResponseSpec actualExchangeResult = ((WebTestClient.RequestHeadersSpec<?>) getResult
.uri(UriComponentsBuilder.fromPath("/api/greetings").queryParam("lang", "en").build().toUriString()))
.exchange();
// Assert
verify(greetingService).fetchByLanguage(eq("en"));
actualExchangeResult.expectStatus().isOk().expectHeader().contentType("text/plain;charset=UTF-8");
Class<String> elementClass = String.class;
StepVerifier.FirstStep<String> createResult = StepVerifier
.create(actualExchangeResult.returnResult(elementClass).getResponseBody());
createResult.expectComplete().verify();
}
}
Diffblue Cover places our new test into the existing test class, ensuring that the previously generated tests still function as expected.
The first difference we notice is the usage of @MockBean for our GreetingService
. This brings us the desired isolation as we’re not dependent on the implementation of the service for the unit test of our controller.
Diffblue Cover then successfully stubs our mock and instructs the mock to return an empty flux. What follows is the similar WebTestClient
test HTTP call with the difference of adding the required URL parameter to the call.
Given the isolated-nature of our code, Diffblue Cover generated an efficient unit test for our Spring WebFlux application.
Asynchronous Testing with StepVerifier
Moving on and leaving the Spring WebFlux web layer behind, we now want to focus on verifying components that return reactive streams (Mono
or Flux
) where we can’t use the WebTestClient
. Testing these components requires tools that can verify each step in an asynchronous sequence, checking the emissions, completions, and errors produced by a reactive stream.
Unlike synchronous, blocking code, where the returned value is immediately available when accessed, asynchronous and reactive code does not provide results instantly.
For this purpose, StepVerifier
—a utility provided by Project Reactor—is particularly effective. It allows developers to write tests that validate the exact behavior of Mono and Flux streams, ensuring that they emit the expected values in the correct order and handle errors as intended.
The test examples above already made use of the StepVerifier
testing utility but with this section we want to take a closer look at it. Therefore, we let Diffblue Cover generate tests for our GreetingService
:
@Service
public class GreetingService {
private final ExternalGreetingClient greetingClient;
public GreetingService(ExternalGreetingClient greetingClient) {
this.greetingClient = greetingClient;
}
public Flux<String> fetchByLanguage(String language) {
return greetingClient.getGreetingByLanguage(language)
.flatMap(response -> {
if (response.isEmpty()) {
return Flux.error(new NoDataFoundException("No greeting available for language: " + language));
} else {
return Flux.just(response);
}
});
}
}
The GreetingService
that our web layer uses to get its data, internally uses an HTTP client itself to get the greetings for each language from a remote system.
When now writing unit tests for our service, we need to use the StepVerifier
to handle the reactive stream return type of Flux.
Let’s see what Diffblue Cover generates for us:
@ContextConfiguration(classes = {GreetingService.class})
@ExtendWith(SpringExtension.class)
@DisabledInAotMode
class GreetingServiceDiffblueTest {
@MockBean
private ExternalGreetingClient externalGreetingClient;
@Autowired
private GreetingService greetingService;
/**
* Test {@link GreetingService#fetchByLanguage(String)}.
* <p>
* Method under test: {@link GreetingService#fetchByLanguage(String)}
*/
@Test
@DisplayName("Test fetchByLanguage(String)")
void testFetchByLanguage() throws AssertionError {
// Arrange
Flux<String> fromIterableResult = Flux.fromIterable(new ArrayList<>());
when(externalGreetingClient.getGreetingByLanguage(Mockito.<String>any())).thenReturn(fromIterableResult);
// Act and Assert
StepVerifier.FirstStep<String> createResult = StepVerifier.create(greetingService.fetchByLanguage("en"));
createResult.expectComplete().verify();
verify(externalGreetingClient).getGreetingByLanguage(eq("en"));
}
}
The generated tests covers the happy-path of our service class to fetch data from a remote service. Due to the isolated nature of our components, Diffblue Cover mocks and collaborators to focus on a single unit for the test.
With StepVerifier.create
, the generated test initiates the reactive stream returned by fetchByLanguage
and defines the expectations for its output. The .expectComplete()
method confirms that the stream completes without emitting any items, aligning with the mocked response of an empty Flux
. Finally, verify
confirms that getGreetingByLanguage
was called with the expected language parameter, ensuring the method interaction is correct.
This use of StepVerifier
allows precise control and verification of the asynchronous stream’s behavior, which is essential for testing reactive code reliably.
Conclusion
Testing Spring WebFlux applications comes with challenges due to its asynchronous, non-blocking nature. With the new support for Spring WebFlux, Diffblue Cover has made it easier than ever for developers to generate and maintain comprehensive, high-quality tests for reactive components.
By integrating Diffblue Cover into your development workflow and automating the test generation process, Diffblue enables teams to focus on building high-performance, scalable applications without sacrificing test coverage or code quality.
With Diffblue Cover’s automated test generation, you can save time, reduce manual effort, and gain greater confidence in the reliability of your Spring WebFlux applications.
Try Diffblue Cover today
See how Diffblue Cover can transform your testing workflow and supercharge your Spring WebFlux projects.