Since Spring Boot 3’s release in 2022, there’s been a large push to using Reactive Messaging, including a planned deprecation for using RestTemplates. After spending some time last year using WebFlux’s Mono publishers to send messages, I found ways using JUnit and Mockito to easily test your code that aren’t too well known. I am in no way an expert in Java Spring as I’ve only been writing it for a couple years, I’m just hoping this post can help at least someone not have as many headaches as I did whilst figuring this all out.
With all of this testing I am using JUnit5 and Mockito as the testing libraries. As these are relatively common libraries to use for Spring projects, I’m only going to focus on these, and not at all because I haven’t done much in using other libraries for testing in Java.
As always, the code examples are under the MIT license, so feel free to CTRL+C & CTRL+P
Unit Testing Controllers
I’ll start off going through the unit tests as they’re possibly the easiest to integrate, and of course we’re first taking a look at unit testing the controllers.
In my example project, I have one controller which method helloWorld() returns the contents of a manager’s getHello() method in the body of a Mono.
In the unit test class, we’ll want to mock the manager using Mockito’s @MockBean annotation on the declaration of the manager. This allows us to queue any dummy response ready when a method specified in the manager is called. To queue a dummy response, we’ll want to use when() for the conditions to trigger a response, which we’ll use a call to the manager’s method (with any parameters using the any() methods from mockito) as a parameter, followed by using a thenReturn() call to return the dummy response.
After this we’ll want to use a WebTestClient object to call the controller, along with any testing we want to carry out after the exchange() call. Here you can use expectStatus() and expectBody() to your hearts content.
And that’s it! That’s all I did to get my controller tests working with no major loops to jump through.
This is an example of the unit test class’s content (I couldn’t quite be bothered to write out all the preamble needed including the imports and such, so you’ll have to make do with writing that bit yourself).
@MockBean private MyManager myManager;
@Autowired private WebTestClient webTestClient;
@Test
void myTest() {
when(myManager.getHello(anyString()).thenReturn(Mono.just("Hello World!")));
webTestClient
.get()
.uri("http://localhost:8080/api/v1/hello")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.consumeWith(
response -> assertEquals("Hello World!", response.getBody())
);
}
As you can see with the snippet above, I’ve also used a consumeWith() that has a lambda as parameter that returns the output of an assertEquals() so we can easily test the contents of the body.
And just like that you’ve written a unit testfor your controller! Let’s move on with the manager now