'Why is MockBean not working in an integrationtest with RabbitMQ?
I have a Spring Boot application with RabbitMQ and I try to test my code in an integration test. My integration test does work in some cases, but not in all cases.
Application code
@Component
@AllArgsConstructor
public class TestQueueListener {
private final TestService testService;
@RabbitListener(queues = "q_test")
public void listen(String value) {
testService.doSomething(value);
}
}
@Service
@Slf4j
public class TestService {
public void doSomething(String value) {
log.info(value);
}
}
Test code
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class Tests {
@Nested
@Order(1)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@ExtendWith(OutputCaptureExtension.class)
class ServiceTest {
@Autowired TestService testService;
@Test
void testDoSomething(CapturedOutput output) {
testService.doSomething("ServiceTestValue");
assertTrue(output.toString().contains("ServiceTestValue"));
}
}
@Nested
@Order(2)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
class ListenerTest {
@Autowired private RabbitTemplate rabbitTemplate;
@MockBean private TestService testService;
@Test
void testListen() {
rabbitTemplate.convertAndSend("q_test", "testValue");
verify(testService, timeout(30000)).doSomething("testValue");
}
}
}
Logs
2022-05-19 10:59:21.527 INFO 102 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Neither @ContextConfiguration nor @ContextHierarchy found for test class [test.Tests$ListenerTest], using SpringBootContextLoader
2022-05-19 10:59:21.528 INFO 102 --- [ main] o.s.t.c.support.AbstractContextLoader : Could not detect default resource locations for test class [test.Tests$ListenerTest]: no resource found for suffixes {-context.xml, Context.groovy}.
2022-05-19 10:59:21.529 INFO 102 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [test.Tests$ListenerTest]: ListenerTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2022-05-19 10:59:21.534 INFO 102 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration test.TestApplication for test class test.Tests$ListenerTest
2022-05-19 10:59:21.536 INFO 102 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
2022-05-19 10:59:21.536 INFO 102 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@7130e7a8, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3fe79b11, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@15f209fc, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@4f2eec7c, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@39d55f96, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3ae974ae, org.springframework.test.context.transaction.TransactionalTestExecutionListener@3fd127e6, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@724716c, org.springframework.test.context.event.EventPublishingTestExecutionListener@149eb7f1, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@65a3edc8, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@537b088d, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@77f0ac9f, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@16b0acaf, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@6c87de1c, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@67b38c61]
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.6)
2022-05-19 10:59:21.634 INFO 102 --- [ main] test.Tests$ListenerTest : Starting Tests.ListenerTest using Java 11.0.10 on runner-grawsgkm-project-663-concurrent-0 with PID 102 (started by root in /builds/test/test-app)
2022-05-19 10:59:21.635 INFO 102 --- [ main] test.Tests$ListenerTest : No active profile set, falling back to 1 default profile: "default"
2022-05-19 10:59:22.307 INFO 102 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [tmprabbit:5672]
2022-05-19 10:59:22.317 INFO 102 --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#4813cd48:0/SimpleConnection@662f5d78 [delegate=amqp://[email protected]:5672/, localPort= 49876]
2022-05-19 10:59:22.336 INFO 102 --- [ main] test.Tests$ListenerTest : Started Tests.ListenerTest in 0.796 seconds (JVM running for 7.761)
2022-05-19 10:59:22.383 INFO 102 --- [ntContainer#0-1] test.TestService : testValue
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 36.706 s <<< FAILURE! - in test.Tests
[ERROR] testListen Time elapsed: 30.053 s <<< FAILURE!
org.mockito.exceptions.verification.WantedButNotInvoked:
Wanted but not invoked:
testService bean.doSomething("testValue");
-> at test.Tests$ListenerTest.testListen(Tests.java:46)
Actually, there were zero interactions with this mock.
at test.Tests$ListenerTest.testListen(Tests.java:46)
Research
The logs show that the real TestService is called, not the mocked one. I thought it has something to do with Spring's context cache, so I enabled DEBUG logs. But apparently there are the right number of contexts in the cache. In extended logs I can also see that the right context is retrieved.
2022-05-19 11:29:42.707 DEBUG 103 --- [ main] c.DefaultCacheAwareContextLoaderDelegate : Storing ApplicationContext [1261461477] in cache under key [[MergedContextConfiguration@3f7e3d48 testClass = Tests.ListenerTest, locations = '{}', classes = '{class test.TestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@31b82e0f, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@10272bbb, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@769c78c2, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@da28d03, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@65afeb6d, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@b27b210, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@7c2b6087], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]
2022-05-19 11:29:42.708 DEBUG 103 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@2c36de3b size = 2, maxSize = 32, parentContextCount = 0, hitCount = 11, missCount = 2]
2022-05-19 11:29:42.712 DEBUG 103 --- [ main] c.DefaultCacheAwareContextLoaderDelegate : Retrieved ApplicationContext [1261461477] from cache with key [[MergedContextConfiguration@3f7e3d48 testClass = Tests.ListenerTest, locations = '{}', classes = '{class test.TestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@31b82e0f, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@10272bbb, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@769c78c2, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@da28d03, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@65afeb6d, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@b27b210, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@7c2b6087], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]
I also added following log statements to see if the used TestService is really a mock. The result was that both variables contain a mock.
log.info("Direct: " + Mockito.mockingDetails(testService).isMock());
log.info("Indirect: " + Mockito.mockingDetails(testQueueListener.testService).isMock());
If I change the order of the tests, all tests pass. However, I don't want to order all my tests as nested classes within one class. That is not readable.
If I call
TestQueueListenerdirecly without RabbitMQ, all tests pass.@Nested @Order(2) @SpringBootTest(webEnvironment = WebEnvironment.NONE) class ListenerTest { @Autowired private TestQueueListener testQueueListener; @MockBean private TestService testService; @Test void testListen() { testQueueListener.listen("testValue"); verify(testService, timeout(30000)).doSomething("testValue"); } }But I also want to test messaging with RabbitMQ.
Question
Why is my integration test failing?
Solution 1:[1]
This works with a minimal winforms application in my system:
System.Diagnostics.Process p = System.Diagnostics.Process.Start("notepad.exe");
Application.Exit();
but also this:
ExecuteAsAdmin("notepad.exe");
Application.Exit()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | phoebus |
