拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 与Spring Cloud Netflix和Feign的集成测试

与Spring Cloud Netflix和Feign的集成测试

白鹭 - 2021-11-24 542 0 0

1.概述

在本文中,我们将探讨Feign Client集成测试

我们将创建一个基本的Open Feign Client,并在WireMock的帮助下为其编写一个简单的集成测试

之后,我们将向客户添加功能区配置,并为其构建集成测试。最后,我们将配置一个Eureka测试容器并测试此设置,以确保我们的整个配置都能按预期工作。

2.Feign Client

要设置Feign Client,我们应该首先添加Spring Cloud OpenFeign Maven依赖项:

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-starter-openfeign</artifactId>

 </dependency>

之后,让我们为模型创建一个Book类:

public class Book {

 private String title;

 private String author;

 }

最后,让我们创建Feign Client界面:

@FeignClient(value="simple-books-client", url="${book.service.url}")

 public interface BooksClient {



 @RequestMapping("/books")

 List<Book> getBooks();



 }

现在,我们有了一个Feign客户端,该客户端从REST服务检索Books列表。现在,让我们继续前进并编写一些集成测试。

3. WireMock

3.1。设置WireMock服务器

如果要测试BooksClient,则需要提供/books端点的模拟服务。我们的客户将对此模拟服务进行呼叫。为此,我们将使用WireMock。

因此,让我们添加WireMock Maven依赖项:

<dependency>

 <groupId>com.github.tomakehurst</groupId>

 <artifactId>wiremock</artifactId>

 <scope>test</scope>

 </dependency>

并配置模拟服务器:

@TestConfiguration

 public class WireMockConfig {



 @Autowired

 private WireMockServer wireMockServer;



 @Bean(initMethod = "start", destroyMethod = "stop")

 public WireMockServer mockBooksService() {

 return new WireMockServer(9561);

 }



 }

现在,我们有一个正在运行的模拟服务器,它在端口9651上接受连接。

3.2。设置模拟

让我们将属性book.service.url添加到指向WireMockServer端口的application-test.yml

book:

 service:

 url: http://localhost:9561

我们还为/books端点准备一个模拟响应get-books-response.json

[

 {

 "title": "Dune",

 "author": "Frank Herbert"

 },

 {

 "title": "Foundation",

 "author": "Isaac Asimov"

 }

 ]

现在,让我们在/books端点上为GET请求配置模拟响应:

public class BookMocks {



 public static void setupMockBooksResponse(WireMockServer mockService) throws IOException {

 mockService.stubFor(WireMock.get(WireMock.urlEqualTo("/books"))

 .willReturn(WireMock.aResponse()

 .withStatus(HttpStatus.OK.value())

 .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)

 .withBody(

 copyToString(

 BookMocks.class.getClassLoader().getResourceAsStream("payload/get-books-response.json"),

 defaultCharset()))));

 }



 }

至此,所有必需的配置均已就绪。让我们继续写我们的第一个测试。

4.我们的首次集成测试

让我们创建一个集成测试BooksClientIntegrationTest

@SpringBootTest

 @ActiveProfiles("test")

 @EnableConfigurationProperties

 @ExtendWith(SpringExtension.class)

 @ContextConfiguration(classes = { WireMockConfig.class })

 class BooksClientIntegrationTest {



 @Autowired

 private WireMockServer mockBooksService;



 @Autowired

 private BooksClient booksClient;



 @BeforeEach

 void setUp() throws IOException {

 BookMocks.setupMockBooksResponse(mockBooksService);

 }



 // ...

 }

至此,我们已经为SpringBootTest配置了WireMockServer准备在BooksClient调用/books端点时返回预定的Books列表。

最后,让我们添加测试方法:

@Test

 public void whenGetBooks_thenBooksShouldBeReturned() {

 assertFalse(booksClient.getBooks().isEmpty());

 }



 @Test

 public void whenGetBooks_thenTheCorrectBooksShouldBeReturned() {

 assertTrue(booksClient.getBooks()

 .containsAll(asList(

 new Book("Dune", "Frank Herbert"),

 new Book("Foundation", "Isaac Asimov"))));

 }

5.与负载均衡集成

现在,通过添加Ribbon提供的负载平衡功能来改进我们的客户端

我们在客户端界面上需要做的就是删除硬编码的服务URL,而是通过服务名称book-service引用该book-service

@FeignClient("books-service")

 public interface BooksClient {

 ...

接下来,添加Netflix Ribbon Maven依赖项:

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>

 </dependency>

最后,在application-test.yml文件中,我们现在应该删除book.service.url ,而是定义功能区listOfServers

books-service:

 ribbon:

 listOfServers: http://localhost:9561

现在,让我们再次运行BooksClientIntegrationTest 。它应该通过,确认新设置可以正常工作。

5.1。动态端口配置

如果我们不想对服务器的端口进行硬编码,则可以将WireMock配置为在启动时使用动态端口。

为此,让我们创建另一个测试配置RibbonTestConfig:

@TestConfiguration

 @ActiveProfiles("ribbon-test")

 public class RibbonTestConfig {



 @Autowired

 private WireMockServer mockBooksService;



 @Autowired

 private WireMockServer secondMockBooksService;



 @Bean(initMethod = "start", destroyMethod = "stop")

 public WireMockServer mockBooksService() {

 return new WireMockServer(options().dynamicPort());

 }



 @Bean(name="secondMockBooksService", initMethod = "start", destroyMethod = "stop")

 public WireMockServer secondBooksMockService() {

 return new WireMockServer(options().dynamicPort());

 }



 @Bean

 public ServerList ribbonServerList() {

 return new StaticServerList<>(

 new Server("localhost", mockBooksService.port()),

 new Server("localhost", secondMockBooksService.port()));

 }



 }

此配置设置了两个WireMock服务器,每个服务器运行在运行时动态分配的不同端口上。此外,它还使用两个模拟服务器配置功能区服务器列表。

5.2。负载平衡测试

现在我们已经配置了Ribbon负载平衡器,让我们确保我们的BooksClient在两个模拟服务器之间正确交替:

@SpringBootTest

 @ActiveProfiles("ribbon-test")

 @EnableConfigurationProperties

 @ExtendWith(SpringExtension.class)

 @ContextConfiguration(classes = { RibbonTestConfig.class })

 class LoadBalancerBooksClientIntegrationTest {



 @Autowired

 private WireMockServer mockBooksService;



 @Autowired

 private WireMockServer secondMockBooksService;



 @Autowired

 private BooksClient booksClient;



 @BeforeEach

 void setUp() throws IOException {

 setupMockBooksResponse(mockBooksService);

 setupMockBooksResponse(secondMockBooksService);

 }



 @Test

 void whenGetBooks_thenRequestsAreLoadBalanced() {

 for (int k = 0; k < 10; k++) {

 booksClient.getBooks();

 }



 mockBooksService.verify(

 moreThan(0), getRequestedFor(WireMock.urlEqualTo("/books")));

 secondMockBooksService.verify(

 moreThan(0), getRequestedFor(WireMock.urlEqualTo("/books")));

 }



 @Test

 public void whenGetBooks_thenTheCorrectBooksShouldBeReturned() {

 assertTrue(booksClient.getBooks()

 .containsAll(asList(

 new Book("Dune", "Frank Herbert"),

 new Book("Foundation", "Isaac Asimov"))));

 }

 }

6.Eureka整合

到目前为止,我们已经看到了如何测试使用Ribbon进行负载平衡的客户端。但是,如果我们的设置使用诸如Eureka之类的服务发现系统,该怎么办?我们应该编写一个集成测试,以确保我们的BooksClient**在这种情况下也能按预期工作**。

为此,我们将运行Eureka服务器作为测试容器。然后,我们启动并在我们的Eureka容器中注册一个模拟book-service 。最后,一旦安装完成,我们就可以对其进行测试。

在继续之前,让我们添加TestcontainersNetflix Eureka Client Maven依赖项:

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

 </dependency>

 <dependency>

 <groupId>org.testcontainers</groupId>

 <artifactId>testcontainers</artifactId>

 <scope>test</scope>

 </dependency>

6.1。 TestContainer设置

让我们创建一个TestContainer配置,以启动我们的Eureka服务器:

public class EurekaContainerConfig {



 public static class Initializer implements ApplicationContextInitializer {



 public static GenericContainer eurekaServer =

 new GenericContainer("springcloud/eureka").withExposedPorts(8761);



 @Override

 public void initialize(@NotNull ConfigurableApplicationContext configurableApplicationContext) {



 Startables.deepStart(Stream.of(eurekaServer)).join();



 TestPropertyValues

 .of("eureka.client.serviceUrl.defaultZone=http://localhost:"

 + eurekaServer.getFirstMappedPort().toString()

 + "/eureka")

 .applyTo(configurableApplicationContext);

 }

 }

 }

如我们所见,上面的初始化程序启动了容器。然后,它暴露Eureka服务器正在监听的端口8761。

最后,在Eureka服务启动之后,我们需要更新eureka.client.serviceUrl.defaultZone属性。这定义了用于服务发现的Eureka服务器的地址。

6.2。注册模拟服务器

现在,我们的Eureka服务器已启动并正在运行,我们需要注册一个模拟books-service 。我们通过简单地创建一个RestController来做到这一点:

@Configuration

 @RestController

 @ActiveProfiles("eureka-test")

 public class MockBookServiceConfig {



 @RequestMapping("/books")

 public List getBooks() {

 return Collections.singletonList(new Book("Hitchhiker's Guide to the Galaxy", "Douglas Adams"));

 }

 }

为了注册此控制器,我们现在要做的就是确保我们的application-eureka-test.ymlspring.application.name属性是books-service,BooksClient接口中使用的服务名称相同。

注意:现在netflix-eureka-client库位于我们的依赖项列表中,默认情况下将使用Eureka进行服务发现。因此,如果我们希望不使用Eureka的以前的测试保持通过,则需要手动将eureka.client.enabled设置为false 。这样,即使库位于路径上, BooksClient也不会尝试使用Eureka来定位服务,而是使用Ribbon配置。

6.3。整合测试

再一次,我们拥有所有需要的配置块,因此让我们将它们全部组合在一起进行测试:

@ActiveProfiles("eureka-test")

 @EnableConfigurationProperties

 @ExtendWith(SpringExtension.class)

 @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

 @ContextConfiguration(classes = { MockBookServiceConfig.class },

 initializers = { EurekaContainerConfig.Initializer.class })

 class ServiceDiscoveryBooksClientIntegrationTest {



 @Autowired

 private BooksClient booksClient;



 @Lazy

 @Autowired

 private EurekaClient eurekaClient;



 @BeforeEach

 void setUp() {

 await().atMost(60, SECONDS).until(() -> eurekaClient.getApplications().size() > 0);

 }



 @Test

 public void whenGetBooks_thenTheCorrectBooksAreReturned() {

 List books = booksClient.getBooks();



 assertEquals(1, books.size());

 assertEquals(

 new Book("Hitchhiker's guide to the galaxy", "Douglas Adams"),

 books.stream().findFirst().get());

 }



 }

此测试中发生了一些事情。让我们一一看一下。

首先, EurekaContainerConfig内部的上下文初始化EurekaContainerConfig启动Eureka服务。

然后, SpringBootTest启动books-service应用程序,该应用程序公开MockBookServiceConfig定义的控制器。

因为Eureka容器和Web应用程序的启动可能需要几秒钟的时间,所以我们需要等到books-service被注册。这发生在setUp测试。

最后,测试方法可以验证BooksClient与Eureka配置结合使用是否正确。

7.结论

在本文中,我们探讨了为Spring Cloud Feign Client编写集成测试的不同方法。我们从一个基本的客户开始,我们在WireMock的帮助下进行了测试。之后,我们继续使用Ribbon添加负载平衡。我们编写了一个集成测试,并确保我们的Feign Client与Ribbon所提供的客户端负载平衡正常工作。最后,我们将Eureka服务发现添加到了组合中。再一次,我们确保我们的客户仍然可以按预期工作。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *