模拟API调用在单元测试用例中的Spring Boot

huangapple go评论73阅读模式
英文:

Mock API calls During Unit Test cases Spring boot

问题

我有两个微服务,微服务A(上下文路径 - /abc)和微服务B(上下文路径 - /def)。

示例URL:test.domain.com/abc/endpoint1,test.domain.com/def/endpoint2

在微服务A的一个API中(test.domain.com/abc/endpoint1),它内部调用了微服务B(/def/endpoint2)->这个内部调用的前缀生成如下
(从请求中提取域名,然后附加/def/endpoint2以进行REST调用,总URL将变为(test.domain.com/def/endpoint2)

问题:当我们从控制器级别开始编写单元测试用例时,我们使用TestRestTemplate
为了进行测试,我们需要使用http://localhost:portnumber/abc/endpoint1进行测试..

现在,def服务的URL也将派生为http://localhost:portnumber/def/endpoint2
如何模拟这个响应(注意:我们不能在相同的端口上使用模拟服务器,否则会出现端口绑定异常)。是否有任何解决方法?

是否有办法在使用TestRestTemplate时设置类似网关的配置,以将http://localhost:portnumber/def/*调用路由到模拟服务器以获取响应,并将http://localhost:portnumber/abc/*用于实际测试的API服务?

英文:

I have two microservices Microservice A ( context path - /abc ) and microservice B (context path - /def )

Example URLs: test.domain.com/abc/endpoint1 ,test.domain.com/def/endpoint2

In one of the apis of Microservice A ( test.domain.com/abc/endpoint1) internally its making call to Microservice B (/def/endpoint2) -> the prefix for this internal call is generated as follows
(Extract the domain from the request and then append /def/endpoint2 to make a rest call the total url will become as (test.domain.com/def/endpoint2)

Problem : When we are writting unit test cases starting controller level we are using TestRestTemplate
For this testing we need to use http://localhost:portnumber/abc/endpoint1 to test ..

Now the url of the def service also will be derived as http://localhost:portnumber/def/endpoint2
How to mock this response ( Note: We cannot use mock server on same port, we will get port binding exception) . Is there any workaround for the same?

Is there any way to have gateway kind of setup while using TestRestTemplate to route http://localhost:portnumber/def/* calls to get response from mockserver and http://localhost:portnumber/abc/* to make the actual API Service under test?

答案1

得分: 1

你可以使用ClientHttpRequestInterceptor来实现这个功能,并且可以操作实际的URI,以便在匹配第二个微服务的路径时调用它。这可能是一个天真的原型实现:

public class UrlRewriter implements ClientHttpRequestInterceptor {
  @Override
  public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    try {
      if (httpRequest.getURI().toString().contains("/def/abc")) {
        HttpRequest modifiedRequest = new MockClientHttpRequest(HttpMethod.GET, new URI("http://localhost:8888/def/abc"));
        return clientHttpRequestExecution.execute(modifiedRequest, bytes);
      } else {
        return clientHttpRequestExecution.execute(httpRequest, bytes);
      }
    } catch (URISyntaxException e) {
      e.printStackTrace();
      return null;
    }
  }
}

然后,你可以为你的测试提供一个RestTemplateBuilder类型的自定义bean,该bean将被TestRestTemplate使用:

@SpringBootTest(webEnvironment = RANDOM_PORT)
public class TestOne {

  @TestConfiguration
  static class TestConfig {

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
      return new RestTemplateBuilder().interceptors(new UrlRewriter());
    }

  }

  @Autowired
  private TestRestTemplate testRestTemplate;

  @Test
  public void test() {
    assertNotNull(testRestTemplate);

    testRestTemplate.getForObject("/abc/endpoint1", String.class);
  }
}
英文:

You could use a ClientHttpRequestInterceptor for this and manipulate the actual URI to call if it matches the path of your second microservice.

This might be a naive protoypish implementation:

public class UrlRewriter implements ClientHttpRequestInterceptor {
  @Override
  public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    try {
      if (httpRequest.getURI().toString().contains("/def/abc")) {
        HttpRequest modifiedRequest = new MockClientHttpRequest(HttpMethod.GET, new URI("http://localhost:8888/def/abc"));
        return clientHttpRequestExecution.execute(modifiedRequest, bytes);
      } else {
        return clientHttpRequestExecution.execute(httpRequest, bytes);
      }

    } catch (URISyntaxException e) {
      e.printStackTrace();
      return null;
    }
  }
}

And then you can provide a custom bean of type RestTemplateBuilder for your test that is picked up by the TestRestTemplate:

@SpringBootTest(webEnvironment = RANDOM_PORT)
public class TestOne {


  @TestConfiguration
  static class TestConfig {

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
      return new RestTemplateBuilder().interceptors(new UrlRewriter());
    }

  }

  @Autowired
  private TestRestTemplate testRestTemplate;

  @Test
  public void test() {
    assertNotNull(testRestTemplate);

    testRestTemplate.getForObject("/abc/endpoint1", String.class);
  }
}

huangapple
  • 本文由 发表于 2020年8月7日 04:13:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63291111.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定