openfeign 的集成测试问题,hystrix 作为断路器,ribbon 作为负载均衡器

Issue with integration tests for openfeign with hystrix as circuit breaker and ribbon as load balancer

我正在使用 spring boot 2.4.2 和 spring cloud 2020.0.1。我正在使用带有 hystrix 的 openfeign 作为断路器。我正在使用 Eureka 进行服务发现。该功能按预期工作,但我无法让测试正常工作。它抛出一个异常

[503] during [GET] to [http://simple-helloworld-api/api/v1/greet/Test?error=false] [HelloWorldFeignClient#greeting(String,Boolean)]: [Load balancer does not contain an instance for the service simple-helloworld-api]

build.gradle

中的依赖项
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-hystrix', version: '2.2.7.RELEASE'
    implementation group: 'io.github.openfeign', name: 'feign-hystrix', version: '11.0'
    implementation group: 'io.github.openfeign', name: 'feign-httpclient', version: '11.0'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation "com.github.tomakehurst:wiremock:2.26.3"
    testImplementation group: 'io.github.openfeign', name: 'feign-ribbon', version: '11.0'
}

伪装客户端

@FeignClient(name = "${feign.name.helloWorldApi}",
        configuration = HelloWorldFeignClientConfiguration.class,
        fallbackFactory = HelloWorldFeignClientFallbackFactory.class)
public interface HelloWorldFeignClient {

    @GetMapping("${helloWorldApi.baseUrl}" + "greet/{name}")
    String greeting(@PathVariable("name") String name, @RequestParam(name = "error", required = false) Boolean error);
}

HelloWorldFeignClientFallbackFactory 是从 org.springframework.cloud.openfeign.FallbackFactory 而不是从 feign.hystrix 导入的。

测试class

@SpringBootTest(classes = {HelloWorldFeignClientTest.FeignConfig.class},
        properties = {"eureka.client.enabled=false", "feign.circuitbreaker.enabled=true"},
    webEnvironment = WebEnvironment.RANDOM_PORT)
class LoadBalancedHelloWorldFeignClientTest {
    private static WireMockServer wireMockServer;

    @Autowired
    private HelloWorldFeignClient helloWorldFeignClient;

    @BeforeAll
    static void setUp() {
        wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort());
        wireMockServer.start();
    }

    @AfterAll
    static void tearDown() {
        wireMockServer.stop();
        wireMockServer = null;
    }

    @Test
    void testHelloWorldFeign_Success_ReturnGreetingMessage() {
        //Arrange
        wireMockServer.stubFor(get(urlPathEqualTo("/api/v1/greet/Test"))
                .willReturn(aResponse()
                        .withStatus(HttpStatus.OK.value())
                        .withHeader("Content-Type", MediaType.TEXT_PLAIN_VALUE)
                        .withBody("Hello!! Test")));

        //Act
        String message = helloWorldFeignClient.greeting("Test", false);

        //Assert
        assertThat(message).isNotNull();
    }

    @Test
    void testHelloWorldFeign_Fail_ReturnNull() {
        wireMockServer.stubFor(get(urlPathEqualTo("/api/v1/greet/.*"))
                .willReturn(aResponse().withStatus(501)));

        String message = helloWorldFeignClient.greeting("Test", true);

        //Hystrix fallback implementation will return null
        assertThat(message).isNull();
    }

    @EnableFeignClients(clients = HelloWorldFeignClient.class)
    @Configuration
    @EnableAutoConfiguration
    @RibbonClient(name = "simple-helloworld-api", configuration = LoadBalancedHelloWorldFeignClientTest.RibbonConfig.class)
    static class FeignConfig {

        @Bean
        public HelloWorldFeignClientFallbackFactory helloWorldFeignClientFallbackFactory() {
            return new HelloWorldFeignClientFallbackFactory();
        }
    }

    @Configuration
    static class RibbonConfig {

        @Bean
        public ServerList<Server> serverList() {
            return new StaticServerList<>(new Server("localhost", wireMockServer.port()));
        }
    }
}

有人可以帮我吗?我已将示例项目推送到 https://github.com/sbhambani/simple-helloworld-api-client.

最后,我已经能够解决测试用例的问题。 SimpleDiscoveryProperties.

中需要设置实例信息

设置实例信息

@BeforeEach
void addServiceInstance() {
    DefaultServiceInstance defaultServiceInstance = new DefaultServiceInstance();
    defaultServiceInstance.setServiceId("simple-helloworld-api");
    defaultServiceInstance.setHost("localhost");
    defaultServiceInstance.setPort(wireMockServer.port());
    Map<String, List<DefaultServiceInstance>> instances = new HashMap<>();
    instances.put("simple-helloworld-api", Collections.singletonList(defaultServiceInstance));
    simpleDiscoveryProperties.setInstances(instances);
}

@AfterEach
void removeServiceInstance() {
    simpleDiscoveryProperties.getInstances().clear();
}