How to wait for @JMSListener annotated method to complete in JUnit
Well, I was hoping I can somehow hook into the Message Driven Pojos lifecycle to do this, but going through other SO questions about async code I came up with a solution based on CountDownLatch
The
@JMSListener
annotaded method should callcountDown()
on a CountDownLatch after all the work is complete:@JmsListener(destination = "dest", containerFactory = "cf")
public void processMessage(TextMessage message) throws JMSException {
//do the actual processing
actualProcessing(message);
//if there's countDownLatch call the countdown.
if(countDownLatch != null) {
countDownLatch.countDown();
}
}In the testMethod
@Test
public void test() throws InterruptedException {
//initialize the countDownLatch and set in on the processing class
CountDownLatch countDownLatch = new CountDownLatch(1);
messageProcessor.setCountDownLatch(countDownLatch);
//sendthemessage
sendSomeMessage();
//wait for the processing method to call countdown()
countDownLatch.await();
verify();
}
The drawback of this solution is that you have to actually change your @JMSListener
annotated method specifically for the integration test
junit spring jms listener
Wrap your listener bean in a proxy (for the test case) and use a latch and verify that the object received is what you expected...
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { So48033124Application.class, So48033124ApplicationTests.TestConfig.class })
public class So48033124ApplicationTests {
@Autowired
private JmsTemplate template;
@Test
public void test() throws Exception {
Foo foo = new Foo("bar");
this.template.convertAndSend("foo", foo);
assertThat(TestConfig.latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(TestConfig.received).isEqualTo(foo);
}
@Configuration
public static class TestConfig {
private static final CountDownLatch latch = new CountDownLatch(1);
private static Object received;
@Bean
public static BeanPostProcessor listenerWrapper() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyListener) {
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
if (invocation.getMethod().getName().equals("listen")) {
received = invocation.getArguments()[0];
latch.countDown();
}
return result;
}
};
if (AopUtils.isAopProxy(bean)) {
((Advised) bean).addAdvice(interceptor);
return bean;
}
else {
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvice(interceptor);
return proxyFactory.getProxy();
}
}
else {
return bean;
}
}
};
}
}
}
EDIT
The above is based on Spring Framework 5 or later which uses Java 8 and provides default implementations for both BeanPostProcessor
methods.
If you are using an earlier version of Spring you will also need
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
The BPP should be static
too.
Writing tests to verify received msg in jms listener (Spring-Boot)
@SpringBootApplication
public class So42803627Application {
public static void main(String[] args) {
SpringApplication.run(So42803627Application.class, args);
}
@Autowired
private JmsTemplate jmsTemplate;
@JmsListener(destination = "foo")
public void handle(String in) {
this.jmsTemplate.convertAndSend("bar", in.toUpperCase());
}
}
and
@RunWith(SpringRunner.class)
@SpringBootTest
public class So42803627ApplicationTests {
@Autowired
private JmsTemplate jmsTemplate;
@Test
public void test() {
this.jmsTemplate.convertAndSend("foo", "Hello, world!");
this.jmsTemplate.setReceiveTimeout(10_000);
assertThat(this.jmsTemplate.receiveAndConvert("bar")).isEqualTo("HELLO, WORLD!");
}
}
Junit @value annotated field usage Java
The below solution worked for me. I used ReflectionTestUtils to set value to property field in each test case method.
ReflectionTestUtils.setField(classToTest, "skipUnderwriting", Boolean.TRUE);
Spring Boot JMSListener blocked by other JMSListener for different destination
It looks like you are using transactions, the transaction won't commit until the @JmsListener
method exits so the other consumer won't see it.
You can't use transactions for this use case.
Hence the @Async
works because the send will be performed in a different transaction.
Related Topics
How to Split Single Row into Multiple Rows in Spark Dataframe Using Java
Mock User Console Input in Junit Test
Calling Mutiple Webservice At Same Time in Spring Java
Reading a Text File Character by Character into a 2D Array in Java
How to Change the Background Color of a Textfield Without Changing the Border in Javafx
Custom Pagination by Array List
Alternative to If-Else Statements or Better Approach
Getting Java.Lang.Classnotfoundexception: Javax.Servlet.Servletcontext in Junit
Spring Jpa/Hibernate Transaction Force Insert Instead of Update
How to Find Out If String Has Already Been Url Encoded
Disable Spring Security Config Class for @Webmvctest in Spring Boot
I Get a Status 200 When Connecting to the Websocket, But It Is an Error
How to Reconnect Kafka Producer Once Closed
Dynamic Placeholder Substitution in Properties in Java
How to Mock JPA Repository'S Save Method in Unit Tests
How to Get Response as Json With Responseentity in Java