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 를 사용하려면 okhttpmockwebserver 모두에 대한 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에서 사용할 수 있습니다.

Junit footer banner