如何使用 spring-cloud-netflix 和 feign 编写集成测试

新手上路,请多包涵

我使用 Spring-Cloud-Netflix 进行微服务之间的通信。假设我有两个服务,Foo 和 Bar,Foo 使用 Bar 的 REST 端点之一。我使用带有 @FeignClient 注释的接口:

 @FeignClient
public interface BarClient {
  @RequestMapping(value = "/some/url", method = "POST")
  void bazzle(@RequestBody BazzleRequest);
}

然后我在 Foo 中有一个服务类 SomeService ,它调用 BarClient

 @Component
public class SomeService {
    @Autowired
    BarClient barClient;

    public String doSomething() {
      try {
        barClient.bazzle(new BazzleRequest(...));
        return "so bazzle my eyes dazzle";
      } catch(FeignException e) {
        return "Not bazzle today!";
      }

    }
}

现在,为了确保服务之间的通信正常,我想构建一个测试,使用 WireMock 之类的东西对假的 Bar 服务器发出真实的 HTTP 请求。测试应确保 feign 正确解码服务响应并将其报告给 SomeService

 public class SomeServiceIntegrationTest {

    @Autowired SomeService someService;

    @Test
    public void shouldSucceed() {
      stubFor(get(urlEqualTo("/some/url"))
        .willReturn(aResponse()
            .withStatus(204);

      String result = someService.doSomething();

      assertThat(result, is("so bazzle my eyes dazzle"));
    }

    @Test
    public void shouldFail() {
      stubFor(get(urlEqualTo("/some/url"))
        .willReturn(aResponse()
            .withStatus(404);

      String result = someService.doSomething();

      assertThat(result, is("Not bazzle today!"));
    }
}

我怎样才能将这样一个 WireMock 服务器注入到 eureka 中,以便 feign 能够找到它并与之通信?我需要什么样的注解魔法?

原文由 Bastian Voigt 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 868
2 个回答

使用 Spring 的 RestTemplate 而不是 feign。 RestTemplate 也可以通过 eureka 解析服务名称,所以你可以这样做:

 @Component
public class SomeService {
   @Autowired
   RestTemplate restTemplate;

   public String doSomething() {
     try {
       restTemplate.postForEntity("http://my-service/some/url",
                                  new BazzleRequest(...),
                                  Void.class);
       return "so bazzle my eyes dazzle";
     } catch(HttpStatusCodeException e) {
       return "Not bazzle today!";
     }
   }
}

使用 Wiremock 比 feign 更容易测试。

原文由 Bastian Voigt 发布,翻译遵循 CC BY-SA 3.0 许可协议

下面是使用 WireMock 测试带有 Feign 客户端和 Hystrix 回退的 SpringBoot 配置的示例。

如果您使用 Eureka 作为服务器发现,您需要通过设置属性 "eureka.client.enabled=false" 来禁用它。

首先,我们需要为我们的应用启用 Feign/Hystrix 配置:

 @SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@FeignClient(
        name = "bookstore-server",
        fallback = BookClientFallback.class,
        qualifier = "bookClient"
)
public interface BookClient {

    @RequestMapping(method = RequestMethod.GET, path = "/book/{id}")
    Book findById(@PathVariable("id") String id);
}

@Component
public class BookClientFallback implements BookClient {

    @Override
    public Book findById(String id) {
        return Book.builder().id("fallback-id").title("default").isbn("default").build();
    }
}

请注意,我们正在为 Feign 客户端指定一个回退类。每次 Feign 客户端调用失败(例如连接超时)时,都会调用 Fallback 类。

为了让测试工作,我们需要配置 Ribbon 负载均衡器(Feign 客户端在发送 http 请求时会在内部使用):

 @RunWith(SpringRunner.class)
@SpringBootTest(properties = {
        "feign.hystrix.enabled=true"
})
@ContextConfiguration(classes = {BookClientTest.LocalRibbonClientConfiguration.class})
public class BookClientTest {

    @Autowired
    public BookClient bookClient;

    @ClassRule
    public static WireMockClassRule wiremock = new WireMockClassRule(
            wireMockConfig().dynamicPort()));

    @Before
    public void setup() throws IOException {
        stubFor(get(urlEqualTo("/book/12345"))
                .willReturn(aResponse()
                        .withStatus(HttpStatus.OK.value())
                        .withHeader("Content-Type", MediaType.APPLICATION_JSON)
                        .withBody(StreamUtils.copyToString(getClass().getClassLoader().getResourceAsStream("fixtures/book.json"), Charset.defaultCharset()))));
    }

    @Test
    public void testFindById() {
        Book result = bookClient.findById("12345");

        assertNotNull("should not be null", result);
        assertThat(result.getId(), is("12345"));
    }

    @Test
    public void testFindByIdFallback() {
        stubFor(get(urlEqualTo("/book/12345"))
                .willReturn(aResponse().withFixedDelay(60000)));

        Book result = bookClient.findById("12345");

        assertNotNull("should not be null", result);
        assertThat(result.getId(), is("fallback-id"));
    }

    @TestConfiguration
    public static class LocalRibbonClientConfiguration {
        @Bean
        public ServerList<Server> ribbonServerList() {
            return new StaticServerList<>(new Server("localhost", wiremock.port()));
        }
    }
}

功能区服务器列表需要与我们的 WireMock 配置的 url(主机和端口)相匹配。

原文由 mladzo 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题