Using Mockito with Spring in JUnit 5

I was very pleased to read that Mockito finally integrates nicely with JUnit 5 via its shiny new MockitoExtension – about time!

As I am playing around with a project based on Spring Framework 5 and JUnit 5 at the moment, I really wanted to bring in Mockito, as well – it’s an awesome mocking framework really.

Boy had I known what’s ahead of me I might have simply switched to Spring Boot, which apparently offers Mockito integration out of the box via @MockBean.

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:

org.springframework.test.context.ContextCustomizerFactory=com.dajudge.playground.support.MockitoContextCustomizerFactory
org.springframework.test.context.TestExecutionListener=com.dajudge.playground.support.MockitoTestExecutionListener
META-INF/spring.factories

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.

@ExtendWith(SpringExtension.class)
@ExtendWith(MockitoExtension.class)
@ContextConfiguration(classes = SpringMockitoTest.TestBean.class)
class SpringMockitoTest {
    @Autowired
    private TestBean testBean;
    @Mock
    private InterfaceToMock mock;

    @Test
    void mockito_integrates_with_spring() {
        when(mock.doIt()).thenReturn("I did it!");
        assertEquals("I did it!", testBean.callMock());
    }

    @Service
    public static class TestBean {
        @Autowired
        private InterfaceToMock mock;

        public String callMock() {
            return mock.doIt();
        }
    }

    public interface InterfaceToMock {
        String doIt();
    }
}
SpringMockitoTest

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.

Conclusion

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.

Leave a Reply

Your email address will not be published. Required fields are marked *