In Java there is technically no way to override a field. When you declare a field with the same name in a subclass, that field shadows the field with the same name in the superclass. So that is one hurdle.
The second hurdle stems from the fact that there is no way in Spring Boot's testing support to turn off mocking of a bean in a subclass if the superclass configures the mocking via @MockBean
.
Thus, the solution is to invert what you are trying to do by having the real bean injected via @Autowired
in the superclass and then turn on mocking for the specific bean type in the subclass.
- Declare the
@Autowired
field in the superclass but do not redeclare the field in the subclass.
- Declare
@MockBean
at the class level in the subclass, specifying which classes (i.e., bean types) to be mocked.
That last step results in the inherited field being injected with a mock in the subclass.
The following two classes demonstrate this technique in practice.
@SpringBootTest(classes = BaseTests.Config.class)
class BaseTests {
@Autowired
protected Service service;
@Test
void service() {
assertThat(service.getMessage()).isEqualTo("real");
}
@Configuration
static class Config {
@Bean
Service service() {
return new Service();
}
}
static class Service {
String getMessage() {
return "real";
}
}
}
@MockBean(classes = BaseTests.Service.class)
class MockBeanTests extends BaseTests {
@BeforeEach
void setUpMock() {
when(service.getMessage()).thenReturn("mock");
}
@Test
@Override
void service() {
assertThat(service.getMessage()).isEqualTo("mock");
}
}