I was very pleased to read that Mockito finally integrates nicely with JUnit 5 via its shiny new MockitoExtension – about time!
But as I started with a barebone Spring project on purpose, to get a closer look at all the nitty gritty details that Spring Boot hides away from you, this seemed like a good way to deepen my understanding of Spring’s dependency injection and test support mechanisms.
How it works
Long story short, I ended up using a combination of a ContextCustomizer and a TestExecutionListener to get things plumbed together. Both interfaces are part of Spring’s test support and are forced into your test context using a META-INF/spring.factories file:
The ContextCustomizerFactory will be counseled by Spring when bean discovery has been finished but before the context is refreshed – a good time to register a couple of new beans for mocks.
Unfortunately Mockito and Spring have contradictory lifecycle concepts: Mockito follows the JUnit paradigm of having an instance of each mock per test method execution, whereas Spring retains the same context for all test methods of a test class.
In order to bring these two lifecycles together, I am not directly registering mock instances in Spring’s test application context – in fact those mock instances don’t even exist, yet, at this point. Instead I register Javassist proxy instances which will later be wired to access the mock instances appropriately – which is exactly the task of the TestExecutionListener. Right before the test runs – at a point where the MockitoExtension has already created and injected the mock instances – the proxies are configured to access these instances for the execution of the test method. And for the next test method they get re-wired again, and so on.
How it’s used
The following test class demonstrates the combined usage of SpringExtension and MockitoExtension integrated nicely with the aforementioned mechanism.
The actual test class doesn’t change at all, but when you don’t provide the ContextCustomizer and the TestExecutionListener then Spring will just complain about not being able to resolve a bean of type InterfaceToMock.
While I am well aware that this is most likely one of the ugliest ways to get the job done, some extensive Googling didn’t yield any useful hints on how to get it done easier or cleaner.
I’d love to hear about your experiences with the combined usage of Spring and Mockito and should you know about a cleaner way to do it, please leave me a message in the comments!
A running example project can be found in this github repo.