Home > Back-end >  How to mock external rest services when writing integration test in spring boot
How to mock external rest services when writing integration test in spring boot

Time:10-05

I have a controller from which gateway(Spring integration) is being called. Inside gateway I have several flows where I'm doing some outboundgateway calls. I've written my integration test as below -

@Tag("integrationtest")
@ExtendWith(SpringExtension.class)
@SpringBootTest(
    classes = MyWebApplication.class,
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTest {

  @LocalServerPort private int port;

  TestRestTemplate testRestTemplate = new TestRestTemplate();
  HttpHeaders headers = new HttpHeaders();

  @Test
  void testEntireApplication() {

    HttpEntity<LoanProvisionRequest> entity =
        new HttpEntity(TestHelper.generateValidLionRequest(), headers);

    ResponseEntity<LoanProvisionResponse> response =
        testRestTemplate.exchange(
            createURLWithPort("/provision"), HttpMethod.POST, entity, LionResponse.class);

    assertEquals(1, response.getBody().getASMCreditScoreResultCd());
  }

  private String createURLWithPort(String uri) {
    return "http://localhost:"   port   "/lion-service/v1/decisions"   uri;
  }
}

It's running the application and proceeding through from controller to the gateway and running the flows as expected. But for the outboundgateway calls it's failing by saying Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://someurl" because it's not able to access the url that's used in the outboundgateway. I want to stub/mock those url somehow. How do I do that?

I tried doing something below in the same class to mock the url -

  MockRestServiceServer mockServer;

  @BeforeEach
  void setUp() throws JsonProcessingException {
    RestTemplate restTemplate = new RestTemplate();
    mockServer = MockRestServiceServer.bindTo(restTemplate).build();
    DecisionResponse decisionResponse = new DecisionResponse();
    creditDecisionResponse.setId("0013478");
    creditDecisionResponse.setResponse(null);
    creditDecisionResponse.setDescription("dummy Response");

    mockServer
        .expect(
            requestTo(
                "http://xyz-some-url:8080/some-other-service/v1/do-decisions/decision"))
        .andExpect(method(HttpMethod.POST))
        .andRespond(
            withStatus(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new ObjectMapper().writeValueAsString(decisionResponse )));
    mockServer.verify();
  }

But still the same error showing and somehow it's not getting called when it's hitting the outboundgateway call inside the gateway flows.

below is the controller code -

  public ResponseEntity<LionResponse> getLionsNames(
      @RequestBody final @Valid LionRequest req,
      BindingResult bindingResult,
      @RequestHeader HttpHeaders httpHeaders)
      throws JsonProcessingException {
      Long dbId = new SequenceGenerator().nextId();

      lionsGateway.processLionRequest(
          MessageBuilder.withPayload(req).build(),
          dbId,
          SourceSystem.ONE.getSourceSystemCode()));

below is the gateway -

@MessagingGateway
public interface LoansGateway {

  @Gateway(requestChannel = "flow.input")
  List<Object> processLoanRequest(
      @Payload Message lionRequest,
      @Header("dbID") Long dbID,
      @Header("sourceSystemCode") String sourceSystemCode);
}

below is the SpringIntegrationConfiguration class -

 @Bean
  public IntegrationFlow flow() {
    return flow ->
        flow.handle(validatorService, "validateRequest")
            .split()
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .scatterGather(
                scatterer ->
                    scatterer
                        .applySequence(true)
                        .recipientFlow(savingLionRequestToTheDB())
                        .recipientFlow(callingANativeMethod())
                        .recipientFlow(callingAExternalService()),
                gatherer -> gatherer.outputProcessor(prepareCDRequest()))
            .gateway(getDecision(), f -> f.errorChannel("lionDecisionErrorChannel"))
            .to(getDataResp());
  }

  public IntegrationFlow callingAExternalService() {
    return flow ->
        flow.handle(
                Http.outboundGateway(externalServiceURL)
                    .httpMethod(HttpMethod.POST)
                    .expectedResponseType(String.class))
            .logAndReply("Cd response");
  }

.... same way I have other flows that are using outboundgateway but I've not wired the Restemplate instance anywhere.

CodePudding user response:

So, you do in your mock server setup:

RestTemplate restTemplate = new RestTemplate();
mockServer = MockRestServiceServer.bindTo(restTemplate).build();

And that's it. The mocked RestTemplate instance is not used anywhere.

The HttpRequestExecutingMessageHandler has a configuration based on the RestTemplate:

/**
 * Create a handler that will send requests to the provided URI using a provided RestTemplate.
 * @param uri The URI.
 * @param restTemplate The rest template.
 */
public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) {

So, you just need to instrument exactly that RestTemplate which you provide for your HTTP outbound gateway.

Right now your mocking code is dead end.

  • Related