2

我正在使用Spring Integration使用HttpRequestExecutingMessageHandler類以定期向REST服務發出後端請求。我想在測試中嘲笑REST服務器,而不是構建模擬服務器,我寧願使用MockRestServiceServer來執行此操作。但是,MockRestServiceServer似乎不會攔截RestTemplate調用,而是他們正在經歷(至http://example.com/)並提高java.net.ConnectException: Connection refused。有沒有辦法迫使HttpRequestExecutingMessageHandler致電MockRestServiceServer,或者我應該重新考慮這個測試策略嗎?爲應用程序使用MockRestServiceServer測試HttpRequestExecutingMessageHandler

配置:

@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", [email protected](fixedDelay = "1000")) 
    public MessageSource<String> httpRequestTrigger() { return new ExpressionEvaluatingMessageSource<>(new LiteralExpression(""), String.class); } 

    @Bean 
    @ServiceActivator(inputChannel="httpRequestChannel", [email protected](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否則真實URL)

測試:

@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) 

UPDATE 改編測試代碼如下,按阿爾喬姆比蘭的評論:

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爲空。

回答

2

我認爲你在這裏很好。只有您錯過了應用程序爲async的事實。 @InboundChannelAdapter定期發送消息到QueueChannel等。但是它在輪詢者的線程中做到了這一點,而不是那個您正在等待驗證的那個人。

作爲一個修復,我認爲你應該通過.receive(10000)方法真的等待httpReplyChannel的回覆。只有在那之後撥打mockServer.verify()

UPDATE

嗯。我會說我們有一個測試案例已經爲你:

<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

+0

現在使用異步,但仍然得到'ConnectException'。該消息似乎通過「httpReplyChannel」,但消息的正文似乎丟失。 –

+0

我認爲在構建mockServer.expect()之後,您必須在測試中手動將'@ InboundChannelAdapter'設置爲'autoStartup =「false」'和'start()'。爲此目的,你應該注入'SourcePollingChannelAdapter' bean。 –

+0

太棒了,這工作正常!有沒有一種方法可以根據屬性注入'autoStartup'屬性,所以對於dev/prod配置文件'autoStartup =「true」'但對於測試'autoStartup =「false」'? –