I have an integration test with @MockBean
, but the bean is not replaced. The real bean is used. Why is the bean not replaced?
Application code
@Component
@AllArgsConstructor
@Slf4j
public class MyListener {
private final AmqpTemplate amqpTemplate;
private final MyService myService;
@RabbitListener(queues = "q_request")
public void listen(@Payload final String value) {
log.info("Listener: {}, {}", myService.hashCode(), value);
final String result = myService.get(value);
amqpTemplate.convertAndSend("test", "response", result);
}
}
@Service
@AllArgsConstructor
@Slf4j
public class MyService {
public String get(final String value) {
log.info("Service: {}, {} ", this.hashCode(), value);
return "realService";
}
}
Test code
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
class ApplicationTest {
@Test
void testContextLoads() {}
}
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@Slf4j
class MyListenerTest {
@Autowired private RabbitTemplate rabbitTemplate;
@MockBean MyService myService;
@TestConfiguration
static class TestRabbitConfig {
@Bean
public Queue responseQueue() {
return new Queue("q_response", false, false, false);
}
@Bean
public Binding responseBinding(final DirectExchange directExchange, final Queue responseQueue) {
return BindingBuilder.bind(responseQueue).to(directExchange).with("response");
}
}
@Test
void testMessaging() {
log.info(
"Test: {}, {}, {}",
myService.hashCode(),
mockingDetails(myService).isMock(),
mockingDetails(myService).isSpy());
when(myService.get("value")).thenReturn("result");
rabbitTemplate.convertAndSend("test", "request", "value");
final Message message = rabbitTemplate.receive("q_response", 10000);
assertNotNull(message);
verify(myService).get("value");
}
}
Logs
The log of MyListenerTest
shows that the bean is a mock (no spy) in my test class, but the real bean is executed.
2024-08-08T11:01:31.861Z INFO 105 --- [ main] test.MyListenerTest : Started MyListenerTest in 0.818 seconds (process running for 4.28)
2024-08-08T11:01:31.877Z INFO 105 --- [ main] test.MyListenerTest : Test: 1719990258, true, false
2024-08-08T11:01:31.952Z INFO 105 --- [ntContainer#0-1] test.MyListener : Listener: 63583816, value
2024-08-08T11:01:31.953Z INFO 105 --- [ntContainer#0-1] test.MyService : Service: 63583816, value
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.982 s <<< FAILURE! - in test.MyListenerTest
[ERROR] test.MyListenerTest.testMessaging Time elapsed: 0.126 s <<< FAILURE!
Wanted but not invoked:
myService bean.get("value");
-> at test.MyService.get(MyService.java:15)
Actually, there were zero interactions with this mock.
at test.MyService.get(MyService.java:15)
at test.MyListenerTest.testMessaging(MyListenerTest.java:62)
Research
-
The tests passed in the last two yours without a problem, but suddenly the test execution order changed (maybe a change in the dockerized build environment).
-
If I change the name of the tests to change the order of test execution, the tests all passes.
-
It looks like the context was cached, but it should not be cached, see Context Caching:
contextCustomizers
(fromContextCustomizerFactory
) – this includes@DynamicPropertySource
methods as well as various features from Spring Boot’s testing support such as@MockBean
and@SpyBean
.
Question
Why is my bean not replaced by @MockBean
, but a mock is created?