I have made a Spring Boot test for testing JMS consumption.
The test looks like this:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class UpdateThingByJmsIntegrationTest {
@Test
@Rollback(false)
public void updateThingByJmsUpdatesDatabase() throws InterruptedException {
final Thing thing = new ThingBuilder().withId(null).build();
final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate.execute(transactionStatus -> {
thingRepository.save(thing);
return thing;
});
final String xml = String.format(
"<thingDto><id>%s</id><name>something else</name><location>somewhere</location></thingDto>",
thing.getId());
jmsMessagingTemplate.convertAndSend(thingUpdateQueue, xml);
Thread.sleep(1500L);
final Thing updatedThing = thingRepository.getOne(thing.getId());
assertNotNull(updatedThing);
assertEquals("something else", updatedThing.getName());
assertEquals("somewhere", updatedThing.getLocation());
}
So, I save a Thing
in the database, then send a JMS message to update the Thing
. Since JMS consumption happens in a separate thread from the test itself, I wait, and then try to verify that the Thing
has been updated.
This works just fine in IntelliJ, but when running it with Maven it fails, due to the thread consuming the JMS message not being able to find the Thing
in the database.
I have tried to output the object hashcode (identifier) of the ThingRepository
in both the test and the code consuming the JMS message, and they come out differently. With IntelliJ they are the same. I suspect this might be part of the problem, but I'm not sure how to avoid it.
I also checked the log output in IntelliJ vs Maven, and I find that maven outputs these lines before the test is even run, which IntelliJ does not. Don't know if it is relevant.
2019-05-13 09:48:53.983 INFO 9271 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2019-05-13 09:48:53.995 INFO 9271 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-05-13 09:48:53.996 INFO 9271 --- [ main] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2019-05-13 09:48:54.000 INFO 9271 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Shutdown initiated...
2019-05-13 09:48:54.001 INFO 9271 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Shutdown completed.
But why would I get a different repository-object in the test and the class under test?
Update:
Turns out this only happens when running the test in question in the same run as another test. In this other test, I have:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class OtherIntegrationTest {
@MockBean
private ThingRepository thingRepository;
It seems this "bleeds" through to my other test, making the context use a mock while my test uses the real deal. Any way to avoid this, or do I have to find an alternative to using @MockBean?
This could be caused by lack of proper test isolation. If the updateThingByJmsUpdatesDatabase
test is working by itself and fails when run as part of the test suite during build e.g. when tests are run with mvn clean install
.
You should verify this by running this single test using Maven:
mvn test -Dtest=ClassName.updateThingByJmsUpdatesDatabase