使用MockRestServiceServer测试HttpRequestExecutingMessageHandler

时间:2016-12-30 00:20:54

标签: java spring unit-testing spring-integration integration-testing

我正在使用Spring Integration开发一个应用程序,该应用程序使用HttpRequestExecutingMessageHandler类定期向REST服务发出后端请求。我想在测试中模拟REST服务器,而不是构建模拟服务器,我宁愿使用MockRestServiceServer来执行此操作。但是,MockRestServiceServer似乎没有拦截RestTemplate来电,相反,它们会经历(到http://example.com/)并提升java.net.ConnectException: Connection refused。有没有办法迫使HttpRequestExecutingMessageHandler拨打MockRestServiceServer,还是应该重新考虑这种测试策略?

应用程序的配置:

@Configuration
public class RestClientTestApplicationConfig {
    @Bean
    @Qualifier("httpRequestChannel")
    public MessageChannel httpRequestChannel() { return new QueueChannel(); }

    @Bean
    @Qualifier("httpReplyChannel")
    public MessageChannel httpReplyChannel() { return new QueueChannel(); }

    @Bean
    public RestTemplate restTemplate() { return new RestTemplate(); }

    @Bean
    @InboundChannelAdapter(value="httpRequestChannel", poller=@Poller(fixedDelay = "1000"))
    public MessageSource<String> httpRequestTrigger() { return new ExpressionEvaluatingMessageSource<>(new LiteralExpression(""), String.class); }

    @Bean
    @ServiceActivator(inputChannel="httpRequestChannel", poller=@Poller(fixedDelay = "1000"))
    public MessageHandler messageHandler(
        RestTemplate restTemplate,
        @Qualifier("httpReplyChannel") MessageChannel messageChannel,
        @Value("${url}") String url
    ) {
        HttpRequestExecutingMessageHandler messageHandler = new HttpRequestExecutingMessageHandler(url, restTemplate);
        messageHandler.setOutputChannel(messageChannel);
        return messageHandler;
    }
} 

urlapplication-test.properties中定义为测试时为http://example.com,否则为真实网址

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class RestClientIntegrationTest {
    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;

    @Before
    public void setup() {
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    public void makesBackendRequest() {
        mockServer.expect(ExpectedCount.once(), MockRestRequestMatchers.requestTo("http://example.com/"))
            .andExpect(MockRestRequestMatchers.method(HttpMethod.GET));

        mockServer.verify();
    }
}

测试结果:

2016-12-29 16:14:36.902 ERROR 16665 --- [ask-scheduler-2] o.s.integration.handler.LoggingHandler   : org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [http://example.com]; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://example.com": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
    at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:409)

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:103)
    at org.springframework.test.web.client.MockRestServiceServer.verify(MockRestServiceServer.java:117)
    at com.restclienttest.RestClientIntegrationTest.makesBackendRequest(RestClientIntegrationTest.java:35)

更新 根据Artem Bilan的评论改编测试代码如下:

    mockServer.expect(ExpectedCount.once(), MockRestRequestMatchers.requestTo("http://example.com/"))
        .andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
        .andRespond(MockRestResponseCreators.withSuccess("example reply", MediaType.TEXT_PLAIN));

    Message<?> message = httpReplyChannel.receive(1001);
    assertNotNull(message);
    assertThat(((ResponseEntity<String>) message.getPayload()).getBody(), is("example reply"));

仍然获得ConnectException并且MockRestServiceServer发送的示例回复似乎无法通过,因为ResponseEntity的正文为空。

1 个答案:

答案 0 :(得分:2)

我认为你在这里很好。只有您错过了应用程序 async 的问题。 @InboundChannelAdapter会定期向QueueChannel发送消息,依此类推。但它在轮询器的主题中就是这样,而不是那个等待验证的主题。

作为修复,我认为您应该通过httpReplyChannel方法等待.receive(10000)中的回复。然后才调用mockServer.verify()

<强>更新

嗯。我已经说过我们已经有了一个测试用例:

<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>

<int-http:outbound-gateway url="/testApps/httpMethod"
                           request-channel="requestChannel"
                           reply-channel="replyChannel"
                           rest-template="restTemplate"
                           expected-response-type="java.lang.String"
                           http-method-expression="payload"/>

<int:channel id="replyChannel">
    <int:queue/>
</int:channel>
@Autowired
private RestTemplate restTemplate;

private MockRestServiceServer mockServer;

@Before
public void setup() {
    this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
}

@Test
public void testDefaultMethod() throws Exception {
    this.mockServer.expect(requestTo("/testApps/httpMethod"))
            .andExpect(method(HttpMethod.POST))
            .andRespond(withSuccess(HttpMethod.POST.name(), MediaType.TEXT_PLAIN));

    this.defaultChannel.send(new GenericMessage<String>("Hello"));
    Message<?> message = this.replyChannel.receive(5000);
    assertNotNull(message);
    assertEquals("POST", message.getPayload());

    this.mockServer.verify();
}

https://github.com/spring-projects/spring-integration/blob/master/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayWithMethodExpressionTests.java