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 TestContextBootstrapper
that 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.