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();
}
我正在使用 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();
}