1. 개요
오늘날 우리는 대부분의 서비스에서 REST API를 호출할 것으로 예상합니다. Spring은 REST 클라이언트 구축을 위한 몇 가지 옵션을 제공하며 WebClient 를 권장 합니다.
이 빠른 사용방법(예제)에서는 WebClient 를 사용하여 API를 호출 하는 서비스를 단위 테스트 하는 방법을 배웁니다 .
2. 조롱
테스트에서 조롱을 위한 두 가지 주요 옵션이 있습니다.
- Mockito 를 사용 하여 WebClient 의 동작을 모방합니다.
- 실제로 WebClient 를 사용 하지만 MockWebServer (okhttp) 를 사용하여 호출하는 서비스를 모의합니다.
3. 모키토 사용하기
Mockito 는 자바에서 가장 많이 사용되는 조롱 라이브러리입니다. 메서드 호출에 대한 미리 정의된 응답을 제공하는 데는 좋지만 유창한 API를 모의할 때 상황이 어려워집니다. 유창한 API에서는 호출 코드와 모의 객체 사이에 많은 객체가 전달되기 때문입니다.
예를 들어, WebClient 를 사용하여 HTTP를 통해 데이터를 가져오는 getEmployeeById 메서드가 있는 EmployeeService 클래스가 있다고 가정해 보겠습니다 .
public class EmployeeService {
public EmployeeService(String baseUrl) {
this.webClient = WebClient.create(baseUrl);
}
public Mono<Employee> getEmployeeById(Integer employeeId) {
return webClient
.get()
.uri("http://localhost:8080/employee/{id}", employeeId)
.retrieve()
.bodyToMono(Employee.class);
}
}
Mockito를 사용하여 이를 조롱할 수 있습니다.
@ExtendWith(MockitoExtension.class)
public class EmployeeServiceTest {
@Test
void givenEmployeeId_whenGetEmployeeById_thenReturnEmployee() {
Integer employeeId = 100;
Employee mockEmployee = new Employee(100, "Adam", "Sandler",
32, Role.LEAD_ENGINEER);
when(webClientMock.get())
.thenReturn(requestHeadersUriSpecMock);
when(requestHeadersUriMock.uri("/employee/{id}", employeeId))
.thenReturn(requestHeadersSpecMock);
when(requestHeadersMock.retrieve())
.thenReturn(responseSpecMock);
when(responseMock.bodyToMono(Employee.class))
.thenReturn(Mono.just(mockEmployee));
Mono<Employee> employeeMono = employeeService.getEmployeeById(employeeId);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole()
.equals(Role.LEAD_ENGINEER))
.verifyComplete();
}
}
보시다시피 체인의 각 호출에 대해 서로 다른 모의 개체를 제공해야 하며, 4개의 서로 다른 when / thenReturn 호출이 필요합니다. 이것은 장황하고 성가시다 . 또한 서비스가 WebClient를 정확히 어떻게 사용하는지에 대한 구현 세부 정보를 알아야 하므로 불안정한 테스트 방법이 됩니다.
그렇다면 WebClient 에 대한 더 나은 테스트를 어떻게 작성할 수 있습니까?
4. MockWebServer 사용
Square 팀에서 만든 MockWebServer 는 HTTP 요청을 수신하고 응답할 수 있는 작은 웹 서버입니다.
테스트 사례에서 MockWebServer 와 상호 작용 하면 코드에서 로컬 Endpoints에 대한 실제 HTTP 호출을 사용할 수 있습니다 . 우리는 의도된 HTTP 상호 작용을 테스트하는 이점을 얻었으며 복잡한 유창한 클라이언트를 조롱하는 문제는 전혀 없습니다.
Spring 팀 은 통합 테스트 작성을 위해 MockWebServer 를 사용 하는 것을 권장합니다 .
4.1. MockWebServer 의존성
MockWebServer 를 사용하려면 okhttp 및 mockwebserver 모두에 대한 Maven 의존성을 pom.xml에 추가해야 합니다.
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
4.2. 테스트에 MockWebServer 추가 하기
MockWebServer 를 사용하여 EmployeeService 를 테스트해 보겠습니다 .
public class EmployeeServiceMockWebServerTest {
public static MockWebServer mockBackEnd;
@BeforeAll
static void setUp() throws IOException {
mockBackEnd = new MockWebServer();
mockBackEnd.start();
}
@AfterAll
static void tearDown() throws IOException {
mockBackEnd.shutdown();
}
}
위의 JUnit 테스트 클래스에서 setUp 및 tearDown 메소드는 MockWebServer 생성 및 종료를 처리합니다.
다음 단계는 실제 REST 서비스 호출의 포트를 MockWebServer의 포트에 매핑하는 것입니다.
@BeforeEach
void initialize() {
String baseUrl = String.format("http://localhost:%s",
mockBackEnd.getPort());
employeeService = new EmployeeService(baseUrl);
}
이제 MockWebServer 가 HttpRequest 에 응답할 수 있도록 스텁을 만들 차례 입니다.
4.3. 응답 스터빙
MockWebServer의 간편한 enqueue 메서드를 사용하여 웹 서버에서 테스트 응답을 Queue에 넣어 봅시다 .
@Test
void getEmployeeById() throws Exception {
Employee mockEmployee = new Employee(100, "Adam", "Sandler",
32, Role.LEAD_ENGINEER);
mockBackEnd.enqueue(new MockResponse()
.setBody(objectMapper.writeValueAsString(mockEmployee))
.addHeader("Content-Type", "application/json"));
Mono<Employee> employeeMono = employeeService.getEmployeeById(100);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole()
.equals(Role.LEAD_ENGINEER))
.verifyComplete();
}
EmployeeService 클래스 의 getEmployeeById(Integer employeeId) 메서드에서 실제 API 호출이 이루어 지면 MockWebServer 는 대기 중인 스텁으로 응답합니다 .
4.4. 요청 확인
또한 MockWebServer 가 올바른 HttpRequest 를 보냈 는지 확인하고 싶을 수도 있습니다 .
MockWebServer 에는 RecordedRequest 의 인스턴스를 반환하는 takeRequest 라는 편리한 메서드가 있습니다 .
RecordedRequest recordedRequest = mockBackEnd.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());
RecordedRequest 를 사용하면 수신 된 HttpRequest 를 확인하여 WebClient 가 올바르게 전송 했는지 확인할 수 있습니다 .
5. 결론
이 기사에서는 WebClient 기반 REST 클라이언트 코드 를 조롱 하는 데 사용할 수 있는 두 가지 주요 옵션을 보여주었습니다 .
Mockito가 작동하고 간단한 예제에 대한 좋은 옵션일 수 있지만 권장되는 접근 방식은 MockWebServer 를 사용하는 것 입니다.
항상 그렇듯이 이 기사의 소스 코드는 GitHub에서 사용할 수 있습니다.