Spring Boot + 云 | Zuul 代理 |集成测试

Posted

技术标签:

【中文标题】Spring Boot + 云 | Zuul 代理 |集成测试【英文标题】:Spring Boot + Cloud | Zuul Proxy | Integration testing 【发布时间】:2016-04-13 03:45:22 【问题描述】:

当使用 Spring Boot 构建微服务时,它非常容易编写广泛且可读性强的集成测试,并使用 MockRestServiceServer 模拟远程服务请求。

有没有办法使用类似的方法对ZuulProxy 执行额外的集成测试?我想要实现的是能够模拟ZuulProxy 将转发到的远程服务器,并验证我所有的ZuulFitlers 的行为是否符合预期。但是,ZuulProxy 正在使用来自 Netflix 的 RestClient(似乎已弃用?),它自然不使用 RestTemplate 可以由 MockRestServiceServer 重新配置,我目前找不到模拟响应的好方法来自远程服务的代理请求。

我有一个微服务负责处理 API 会话密钥的创建,然后将类似于 API 网关。使用 Zuul Proxy 转发到底层暴露的服务,Zuul Filters 会检测 Session key 是否有效。因此,集成测试会创建一个有效的会话,然后转发到一个假端点,例如“集成/测试”。

可以通过在@WebIntegrationTest 上设置配置属性来指定“集成/测试”是一个新端点,我可以成功模拟所有通过RestTemplate 处理但不是Zuul 转发的服务。

实现模拟前向目标服务的最佳方法是什么?

【问题讨论】:

【参考方案1】:

查看WireMock。我一直在使用它对我的 Spring Cloud Zuul 项目进行集成级别测试。

import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class TestClass 
    @Rule
    public WireMockRule serviceA = new WireMockRule(WireMockConfiguration.options().dynamicPort());

    @Before
    public void before() 
        serviceA.stubFor(get(urlPathEqualTo("/test-path/test")).willReturn(aResponse()
            .withHeader("Content-Type", "application/json").withStatus(200).withBody("serviceA:test-path")));
    

    @Test
    public void testRoute() 
        ResponseEntity<String> responseEntity = this.restTemplate.getForEntity("/test-path/test", String.class);
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);

        serviceA.verify(1, getRequestedFor(urlPathEqualTo("/test-path/test")));
    

【讨论】:

是否需要添加任何配置才能使其正常工作?我正在尝试做类似的事情,但 Zuul 没有选择路线。我得到了这个异常Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: strategie。我想这来自 Zuul 配置,期望来自服务发现服务器(在本例中为 Eureka)的信息,以将调用路由到正确的 ip。【参考方案2】:

接受的答案具有主要思想。但我在某些方面挣扎,直到找出问题所在。所以我想用 Wiremock 来展示一个更完整的答案。

测试:

@ActiveProfiles("test")
@TestPropertySource(locations = "classpath:/application-test.yml")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 5001)
public class ZuulRoutesTest 

    @LocalServerPort
    private int port;

    private TestRestTemplate restTemplate = new TestRestTemplate();

    @Before
    public void before() 

        stubFor(get(urlPathEqualTo("/1/orders/")).willReturn(aResponse()
                .withHeader("Content-Type", MediaType.TEXT_html_VALUE)
                .withStatus(HttpStatus.OK.value())));
    

    @Test
    public void urlOrders() 
        ResponseEntity<String> result = this.restTemplate.getForEntity("http://localhost:"+this.port +"/api/orders/", String.class);
        assertEquals(HttpStatus.OK, result.getStatusCode());

        verify(1, getRequestedFor(urlPathMatching("/1/.*")));
    

还有application-test.yml

zuul:
  prefix: /api
  routes:
    orders:
      url: http://localhost:5001/1/
    cards:
      url: http://localhost:5001/2/

这应该可行。

但是 Wiremock 对我有一些限制。如果您在不同端口上运行具有不同主机名的代理请求,如下所示:

zuul:
  prefix: /api
  routes:
    orders:
      url: http://lp-order-service:5001/
    cards:
      url: http://lp-card-service:5002/

在同一端口上运行的 localhost Wiremock 将无法为您提供帮助。我仍在尝试找到一个类似的集成测试,我可以在其中模拟来自 Spring 的 Bean,并阅读 Zuul 代理在发出请求调用之前选择的 url 路由。

【讨论】:

我认为配置系统的整个想法是能够为“生产”配置设置覆盖,并在测试期间将其指向 localhost 模拟而不是不同的域。否则,您正在研究更多的端到端测试。因此,我不会说这是 Wiremock 的限制,而是可能需要不同方法的设置?例如,您可能希望对模拟服务器的集成测试使用不同的配置文件 @Dherik 不是您要找的吗?只需定义两个WireMockRules 并将它们分别存根wiremock.org/docs/junit-rule @Dherik 你应该能够通过使用 url 作为 forward://service-test-A 来解决这个限制,只是为了在 application-test.yml 中进行测试

以上是关于Spring Boot + 云 | Zuul 代理 |集成测试的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + 云 | Zuul 代理 |集成测试

Spring Boot - 无法通过 Zuul 代理获得 Keycloak 授权 api 的响应

Zuul代理oAuth2在Spring Boot中未经授权

Dockerized Spring boot 和 Zuul

Spring Boot 微服务 com.netflix.zuul.exception.ZuulException:转发错误

使用 Spring boot、Eureka、Zuul、Spring Oauth 创建 OAuth 安全微服务的问题