Spring Boot 1.5.4.RELEASE

If you have a unit test class that extends AbstractTransactionalJUnit4SpringContextTests and you annotate beans in your test class with @MockBean, all of those beans will be null within your test class. However, the mock beans WERE correctly injected into the class I was testing.

If the test does NOT extend that base class, the Mock beans are correctly injected into the test.

There is also a workaround to this issue (similar to the one mentioned in the issue : https://github.com/spring-projects/spring-boot/issues/7689)

If extending the base class, you must explicitly include the MockitoTestExecutionListner:

@TestExecutionListeners(MockitoTestExecutionListener.class)
public class DemoApplicationTestsWithListner extends AbstractTransactionalJUnit4SpringContextTests{

I would argue that mock beans should be injected, regardless of which spring base class they extend from.

I have created a small demo project with three unit tests: One that demonstrates the problem and then two tests that work correctly.

https://github.com/tkvangorder/mockbeanbug

Comment From: snicoll

I would argue that mock beans should be injected, regardless of which spring base class they extend from.

You're missing a point. Those features exist because a @TestExecutionListener was registered behind the scenes for you (the code you provided basically). AbstractTransactionalJUnit4SpringContextTests is a Spring Framework base class and @MockBean, as you know, is a Spring Boot feature.

I think it's not fair to extend from a completely separate infrastructure and expect a feature somewhere else to work out-of-the-box.

Having said that, we can use this issue to improve the documentation about this.

Comment From: tkvangorder

@snicoll Thanks for the quick response, in the context of Spring Framework vs Spring Boot, makes sense. Some documentation around this would definitely help, as this one tripped me up for several hours.

I guess the two remaining questions I have are:

How does Spring Boot implicitly add the TestExecutionListeners when a unit tests does not extend from the a core Spring Framework base class?

And is it because the base class explicitly defines its own set of listeners that the implicit ones are no longer in play?

Comment From: snicoll

How does Spring Boot implicitly add the TestExecutionListeners when a unit tests does not extend from the a core Spring Framework base class?

Via one of the test annotations (@SpringBootTest or any of the slice annotation). It will tune the TestContextBootstrapperthat will make sure, among other things, to register the listener.

And is it because the base class explicitly defines its own set of listeners that the implicit ones

MockitoTestExecutionListener isn't an implicit one so I am not sure that question is relevant. Again, that feature is defined outside of the Test Context Framework.

Comment From: tkvangorder

Maybe "implicit" is the wrong word, after debugging through the test project I linked above:

If you do not extend any of the Spring Framework's base testing classes, the SpringBootTestContextBootstrapper will use the default set of of TestExecutionListeners:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE, classes= {MyConfiguration.class})
public class DemoApplicationTests {

Uses this set of listeners:

 class org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener
 class org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
 class org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener
 class org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener
 class org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener
 class org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener
 class org.springframework.test.context.web.ServletTestExecutionListener
 class org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener
 class org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener
 class org.springframework.test.context.support.DirtiesContextTestExecutionListener
 class org.springframework.test.context.transaction.TransactionalTestExecutionListener
 class org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener

If you use @SpringBootTest in combination with a test that extends the core Spring framework's base testing classes, the bootstrapper does NOT use the default listeners:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE, classes= {MyConfiguration.class})
public class DemoApplicationTests extends AbstractTransactionalJUnit4SpringContextTests{

Uses this set of listeners:

org.springframework.test.context.web.ServletTestExecutionListener
org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener 
org.springframework.test.context.support.DependencyInjectionTestExecutionListener   
org.springframework.test.context.support.DirtiesContextTestExecutionListener    
org.springframework.test.context.transaction.TransactionalTestExecutionListener
org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener

It was my expectation that because I included @SpringBootTest, I would get the correct MockBean behavior. I think this is going to be a common issue as legacy spring application are migrated to Spring boot and those applications start to leverage some of the new Spring Boot testing features.

Comment From: tkvangorder

@snicoll I noticed that my initial comments did not clearly state that I AM using the @SpringBootTest annotation in combination with a test that extends a core spring framework testing class. Please see comment above, not trying to be a pain in the ass, maybe just not articulating my issue well enough.

Comment From: snicoll

It was my expectation that because I included @SpringBootTest, I would get the correct MockBean behavior. I

I already replied to that, isn't it?

I think it's not fair to extend from a completely separate infrastructure and expect a feature somewhere else to work out-of-the-box.

If you have a phrasing that makes it more clear, I am happy to review a PR. Using a base class that setup the test + an annotation is not supported (again, independently of this feature: either you use a base class or you use a bootstrap test annotation).

Comment From: tkvangorder

OK, I think I understand now, I appreciate your time. I suspect others may run into this issue as they migrate older spring application to use some of the new spring boot test features. It might be worthy of a release note.

Comment From: uniquejava

I met this issue as well which stumbled us for several days.

These are my requirements (not a hello world, but in some real production scenarios): 1. SpringBootTest is required (as I would like to use all the good stuff from it like @Autowired) 2. All tests should rollback db transaction after completion 3. Some external service should be mocked.

I didn't realize its the AbstractTransactionalJUnit4SpringContextTests issue. I searched google with these keywords.

SpringBootTest JUnit5, Mockit, NullPointerException

The stack-overflow answers suggest annotate my method with @Test from Jupiter. But I already done that.

I read through spring boot official docs for any clue. No luck.

Fortunately I came here and found the solution! Thank you @tkvangorder

I would like to know in 2020, what's the recommended approach to test with spring-boot2 and mockito and junit5? Maybe AbstractTransactionalJUnit4SpringContextTests should be renewed with some junit5 base class. The name is quite confusing. @snicoll

Thank you.