2

Я работаю над приложением Spring Integration, которое использует класс HttpRequestExecutingMessageHandler, чтобы делать запросы на бэкэнд службе REST через равные промежутки времени. Я хотел бы издеваться над сервером REST при тестировании, а вместо того, чтобы строить макетный сервер, я бы предпочел использовать MockRestServiceServer для этого. Однако MockRestServiceServer, кажется, не перехватывает RestTemplate звонки, а вместо этого они проходят (до http://example.com/) и поднимают java.net.ConnectException: Connection refused. Есть ли способ заставить HttpRequestExecutingMessageHandler позвонить по телефону MockRestServiceServer или мне следует пересмотреть эту стратегию тестирования?Тестирование 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", [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; 
    } 
} 

(url определяется в application-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 - null.

ответ

2

Я думаю, что вы здесь хорошо. Только проблема, что вы упустили тот факт, что ваша заявка async. @InboundChannelAdapter периодически отправляет сообщения в QueueChannel и так далее. Но это происходит в потоке poller, а не в том, где вы ожидаете проверки.

Как исправить, я думаю, вы должны действительно подождать ответа в httpReplyChannel с помощью его метода .receive(10000). И только после этого позвоните mockServer.verify().

UPDATE

Hm. Я бы сказал, что у нас есть тест-случай для вас уже:

<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

Я думаю, что вы должны сделать свой '@ InboundChannelAdapter' как' autoStartup = "false" и 'start()' он вручную в тесте после построения mockServer.expect() '. Для этого вам следует добавить компонент SourcePollingChannelAdapter. –

+0

Отлично, это работает отлично! Есть ли способ внедрить свойство 'autoStartup' на основе свойств, так что для профилей dev/prod' autoStartup = "true" ', но для test' autoStartup = "false" '? –

Смежные вопросы