1. 개요
이 기사에서는 Feign Client 의 통합 테스트 를 살펴볼 것 입니다.
우리는 기본 만듭니다 열기 척하기 클라이언트 있는 우리는 간단한 통합 테스트 쓸 것이다 의 도움으로 WireMock을 .
그런 다음 클라이언트에 리본 구성을 추가하고 이에 대한 통합 테스트도 빌드합니다. 마지막으로 Eureka 테스트 컨테이너를 구성 하고이 설정 을 테스트하여 전체 구성이 예상대로 작동하는지 확인합니다.
2. Feign 클라이언트
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();
}
이제 REST 서비스에서 도서 List을 검색하는 Feign Client가 있습니다. 이제 앞으로 나아가서 몇 가지 통합 테스트를 작성해 보겠습니다.
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. Mock 설정
의이 속성 추가하자 book.service.url을 우리에게 응용 프로그램 test.yml의 받는 사람을 가리키는 WireMockServer의 포트 :
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(mockBooksSerfvice);
}
// ...
}
이 시점에서, 우리는이 SpringBootTest가 로 구성 WireMockServer 의 미리 정의 된 List을 반환 할 준비가 책 때 / 책 에 의해 호출 엔드 포인트 BooksClient .
마지막으로 테스트 방법을 추가해 보겠습니다.
@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로 서비스를 참조하는 것입니다 .
@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을 제거 하고 대신 Ribbon 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 서버를 설정하며, 각각은 런타임에 동적으로 할당 된 다른 포트에서 실행됩니다. 또한 두 개의 모의 서버로 리본 서버 List을 구성합니다.
5.2. 부하 분산 테스트
이제 리본로드 밸런서가 구성 되었으므로 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와 같은 서비스 검색 시스템을 사용한다면 어떨까요? 이러한 컨텍스트에서도 BooksClient 가 예상대로 작동 하는지 확인하는 통합 테스트를 작성해야합니다 .
이를 위해 Eureka 서버를 테스트 컨테이너로 실행합니다 . 그런 다음 Eureka 컨테이너를 사용 하여 모의 도서 서비스 를 시작하고 등록합니다 . 마지막으로이 설치가 완료되면 테스트를 실행할 수 있습니다.
계속 진행하기 전에 Testcontainers 및 Netflix 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 설정
Eureka 서버를 가동 할 TestContainer 구성을 만들어 보겠습니다.
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 서버가 가동되고 실행 중이므로 mock 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"));
}
}
우리가이 컨트롤러를 등록하기 위해 지금해야 할 일은, 있는지 확인하는 것입니다 spring.application.name의 우리의 재산 응용 프로그램 - 유레카 - test.yml이 있습니다 책 서비스, 에 사용되는 서비스 이름과 같은 BooksClient의 인터페이스 .
참고 : 이제 netflix-eureka-client 라이브러리가 의존성 List에 있으므로 서비스 검색에 기본적으로 Eureka가 사용됩니다. 따라서 Eureka를 사용하지 않는 이전 테스트를 계속 통과 하려면 eureka.client.enabled를 false 로 수동 설정해야합니다 . 이렇게하면 라이브러리가 경로에 있더라도 BooksClient 는 서비스를 찾기 위해 Eureka를 사용하지 않고 대신 리본 구성을 사용합니다.
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 내부의 컨텍스트 이니셜 라이저 가 Eureka 서비스를 시작합니다.
그런 다음 SpringBootTest 는 MockBookServiceConfig에 정의 된 컨트롤러를 노출하는 책 서비스 애플리케이션을 시작합니다 .
때문에 유레카 컨테이너의 시작 및 웹 응용 프로그램이 몇 초 정도 걸릴 수 있습니다 , 우리는 때까지 기다릴 필요가 책 서비스가 등록됩니다. 이것은에서 일어나는 의 setUp 시험.
마지막으로 테스트 방법은 BooksClient가 실제로 Eureka 구성과 함께 올바르게 작동하는지 확인합니다.
7. 결론
이 기사에서는 Spring Cloud Feign Client에 대한 통합 테스트를 작성할 수있는 다양한 방법을 살펴 보았습니다 . 우리는 WireMock의 도움으로 테스트 한 기본 클라이언트로 시작했습니다. 그 후 리본으로로드 밸런싱을 추가했습니다. 통합 테스트를 작성하고 Feign Client가 Ribbon에서 제공하는 클라이언트 측 부하 분산과 올바르게 작동하는지 확인했습니다. 마지막으로 유레카 서비스 검색을 믹스에 추가했습니다. 그리고 다시 우리는 클라이언트가 예상대로 작동하는지 확인했습니다.
- https://docs.spring.io/spring-framework/docs/current/reference/html
- https://www.baeldung.com/spring-cloud-feign-integration-tests
'Java' 카테고리의 다른 글
Spring Cloud OpenFeign 소개 (0) | 2021.03.09 |
---|---|
Feign 클라이언트 Timeout 설정 방법 (0) | 2021.03.09 |
Spring WebClient 호출 로깅 (0) | 2021.03.08 |
Spring Boot에서 테스트 (0) | 2021.03.08 |
Java 8 forEach 사용방법 (0) | 2021.03.08 |