为什么在 Spring Boot 中测试 JMS Listener 时我的 MockBeans 和 MockRestServiceServer 没有返回正确的响应
Why are my MockBeans and MockRestServiceServer not returning proper responses when testing JMS Listener in Spring Boot
我在尝试使用 Mockito
和 MockRestServiceServer
集成测试我的 JMS 侦听器时遇到问题。即使我使用了正确的 Mockito.when
注释,它们也会显示为 null,并且 MockRestServiceServer
就像没有被调用一样。如果我改为针对 jms 侦听器调用的 myService
组件进行测试,模拟和 MockRestServiceServer
调用将按预期工作,这令人费解。我正在连接到嵌入式 ActiveMQ 代理进行测试,我正在使用 Spring Boot 2.2。8.RELEASE 和 JDK 8.x 如果有帮助的话。
这是 JMS 侦听器 Class
@Component
public class MyJmsListener {
@Autowired
private MyService myService;
@JmsListener(
destination = "${jms.queue}",
containerFactory = "myJmsListenerContainerFactory"
)
public void receive(Message<String> message) {
myService.process(message);
}
}
这是 JMS 侦听器测试 Class
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class JmsListenerTest {
...
@MockBean
private AuthorizationService authorizationService;
...
@Autowired
private MockRestServiceServer mockRestServiceServer;
@Autowired
private JmsTemplate listenerTestJmsTemplate;
@Value("${jms.queue}")
private String testDestination;
...
@Test
public void testListener() throws IOException, URISyntaxException, InterruptedException {
//ARRANGE
String payloadPath = "classpath:payloads/listenerPayload.json";
String payload = new String(Files.readAllBytes(ResourceUtils.getFile(payloadPath).toPath()));
String testAuth = "auth";
Mockito.when(authorizationService.generateTicket(Mockito.any(Headers.class), Mockito.eq("9130353887051456")))
.thenReturn(testAuth);
String extPayloadPath = "classpath:payloads/revokeCancelAutoRenewRequestApi.json";
String extPayload = new String(Files.readAllBytes(ResourceUtils.getFile(extPayloadPath).toPath()));
mockRestServiceServer.expect(ExpectedCount.once(), MockRestRequestMatchers.requestTo(new URI("/test/v3/subscriptions/400367048/something")))
.andExpect(MockRestRequestMatchers.content().string(extPayload))
.andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, testAuth))
.andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK));
//ACT
listenerTestJmsTemplate.convertAndSend(testDestination, payload);
//ASSERT
mockRestServiceServer.verify();
Assert.assertTrue(JmsListenerWrapperConfiguration.latch.await(5, TimeUnit.SECONDS));
}
...
}
我有一个 JmsListenerWrapperConfiguration,它允许我将倒计时闩锁包装到 jms 侦听器中。
@Configuration
@Profile("test")
public class JmsListenerWrapperConfiguration {
public static final CountDownLatch latch = new CountDownLatch(1);
@Bean
public JmsTemplate listenerTestjmsTemplate(ActiveMQConnectionFactory activeMQConnectionFactory){
JmsTemplate jmsTemplate = new JmsTemplate(activeMQConnectionFactory);
return jmsTemplate;
}
/**
* Wrap the JMS Listeners with a count down latch that will allow us to unit test them.
* @return The bean post processor that will wrap the JMS Listener.
*/
@Bean
public static BeanPostProcessor listenerWrapper() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyJmsListener) {
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
if (invocation.getMethod().getName().equals("listen")) {
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;
}
}
};
}
}
这里定义了 MockRestServiceServer 配置。
@Configuration
@Profile("test")
public class MockRestServiceServerConfiguration {
@Bean
public MockRestServiceServer mockRestServiceServer(RestTemplate restTemplate) {
MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate);
MockRestServiceServer server = builder.bufferContent().build();
return server;
}
}
我看到的错误如下。
java.lang.AssertionError: Further request(s) expected leaving 1 unsatisfied expectation(s).
0 request(s) executed.
at org.springframework.test.web.client.AbstractRequestExpectationManager.verify(AbstractRequestExpectationManager.java:159)
at org.springframework.test.web.client.MockRestServiceServer.verify(MockRestServiceServer.java:116)
更新
我一直在调试,当然测试是 运行 在线程 [main] 上,而 JMS 侦听器是 运行 在线程 [DefaultMessageListenerContainer-1] 上,所以我的问题就变成了,当 mocks/verifications 需要被单独的线程使用时,我们应该如何处理 Mockito 模拟?
事实证明,MockRestServiceServer
需要在闩锁等待后进行验证,如下面的代码所示。
Assert.assertTrue(JmsListenerWrapperConfiguration.latch.await(5, TimeUnit.SECONDS));
mockRestServiceServer.verify();
我在尝试使用 Mockito
和 MockRestServiceServer
集成测试我的 JMS 侦听器时遇到问题。即使我使用了正确的 Mockito.when
注释,它们也会显示为 null,并且 MockRestServiceServer
就像没有被调用一样。如果我改为针对 jms 侦听器调用的 myService
组件进行测试,模拟和 MockRestServiceServer
调用将按预期工作,这令人费解。我正在连接到嵌入式 ActiveMQ 代理进行测试,我正在使用 Spring Boot 2.2。8.RELEASE 和 JDK 8.x 如果有帮助的话。
这是 JMS 侦听器 Class
@Component
public class MyJmsListener {
@Autowired
private MyService myService;
@JmsListener(
destination = "${jms.queue}",
containerFactory = "myJmsListenerContainerFactory"
)
public void receive(Message<String> message) {
myService.process(message);
}
}
这是 JMS 侦听器测试 Class
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class JmsListenerTest {
...
@MockBean
private AuthorizationService authorizationService;
...
@Autowired
private MockRestServiceServer mockRestServiceServer;
@Autowired
private JmsTemplate listenerTestJmsTemplate;
@Value("${jms.queue}")
private String testDestination;
...
@Test
public void testListener() throws IOException, URISyntaxException, InterruptedException {
//ARRANGE
String payloadPath = "classpath:payloads/listenerPayload.json";
String payload = new String(Files.readAllBytes(ResourceUtils.getFile(payloadPath).toPath()));
String testAuth = "auth";
Mockito.when(authorizationService.generateTicket(Mockito.any(Headers.class), Mockito.eq("9130353887051456")))
.thenReturn(testAuth);
String extPayloadPath = "classpath:payloads/revokeCancelAutoRenewRequestApi.json";
String extPayload = new String(Files.readAllBytes(ResourceUtils.getFile(extPayloadPath).toPath()));
mockRestServiceServer.expect(ExpectedCount.once(), MockRestRequestMatchers.requestTo(new URI("/test/v3/subscriptions/400367048/something")))
.andExpect(MockRestRequestMatchers.content().string(extPayload))
.andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, testAuth))
.andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK));
//ACT
listenerTestJmsTemplate.convertAndSend(testDestination, payload);
//ASSERT
mockRestServiceServer.verify();
Assert.assertTrue(JmsListenerWrapperConfiguration.latch.await(5, TimeUnit.SECONDS));
}
...
}
我有一个 JmsListenerWrapperConfiguration,它允许我将倒计时闩锁包装到 jms 侦听器中。
@Configuration
@Profile("test")
public class JmsListenerWrapperConfiguration {
public static final CountDownLatch latch = new CountDownLatch(1);
@Bean
public JmsTemplate listenerTestjmsTemplate(ActiveMQConnectionFactory activeMQConnectionFactory){
JmsTemplate jmsTemplate = new JmsTemplate(activeMQConnectionFactory);
return jmsTemplate;
}
/**
* Wrap the JMS Listeners with a count down latch that will allow us to unit test them.
* @return The bean post processor that will wrap the JMS Listener.
*/
@Bean
public static BeanPostProcessor listenerWrapper() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyJmsListener) {
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
if (invocation.getMethod().getName().equals("listen")) {
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;
}
}
};
}
}
这里定义了 MockRestServiceServer 配置。
@Configuration
@Profile("test")
public class MockRestServiceServerConfiguration {
@Bean
public MockRestServiceServer mockRestServiceServer(RestTemplate restTemplate) {
MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate);
MockRestServiceServer server = builder.bufferContent().build();
return server;
}
}
我看到的错误如下。
java.lang.AssertionError: Further request(s) expected leaving 1 unsatisfied expectation(s).
0 request(s) executed.
at org.springframework.test.web.client.AbstractRequestExpectationManager.verify(AbstractRequestExpectationManager.java:159)
at org.springframework.test.web.client.MockRestServiceServer.verify(MockRestServiceServer.java:116)
更新
我一直在调试,当然测试是 运行 在线程 [main] 上,而 JMS 侦听器是 运行 在线程 [DefaultMessageListenerContainer-1] 上,所以我的问题就变成了,当 mocks/verifications 需要被单独的线程使用时,我们应该如何处理 Mockito 模拟?
事实证明,MockRestServiceServer
需要在闩锁等待后进行验证,如下面的代码所示。
Assert.assertTrue(JmsListenerWrapperConfiguration.latch.await(5, TimeUnit.SECONDS));
mockRestServiceServer.verify();