1. 概要

最近では、ほとんどのサービスでRESTAPIを呼び出すことを期待しています。 SpringにはRESTクライアントを構築するためのいくつかのオプションがあり、WebClientをお勧めします

このクイックチュートリアルでは、WebClientを使用してAPIを呼び出す単体テストサービスの方法を学習します。

2. 嘲笑

テストでモックするための2つの主なオプションがあります。

  • Mockito を使用して、WebClientの動作を模倣します
  • 実際にはWebClientを使用しますが、 MockWebServer(okhttp)を使用して呼び出すサービスをモックします。

3. Mockitoを使用する

Mockito は、Java用の最も一般的なモックライブラリです。 メソッド呼び出しに対して事前定義された応答を提供するのは得意ですが、流暢なAPIをモックする場合は困難になります。 これは、流暢なAPIでは、多くのオブジェクトが呼び出し元のコードとモックの間を通過するためです。

たとえば、[X23X]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と対話することで、コードでローカルエンドポイントへの実際のHTTP呼び出しを使用できるようになります。 意図したHTTPインタラクションをテストするという利点があり、複雑で流暢なクライアントをモックするという課題はありません。

使用する MockWebServer は、統合テストを作成するためにSpringTeamによって推奨されています。

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をテストに追加する

EmployeeServiceMockWebServerでテストしてみましょう。

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);
}

次に、MockWebServerHttpRequestに応答できるようにスタブを作成します。

4.3. 応答をスタブする

MockWebServerの便利なエンキューメソッドを使用して、Webサーバーでテスト応答をキューに入れましょう。

@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();
}

実際のAPI呼び出しがgetEmployeeById(Integer employeeId)メソッドから EmployeeService クラスで行われると、MockWebServerはキューに入れられたスタブ[X193X ]。

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クライアントコードで使用できる2つの主要なオプションを示しました。

Mockitoは機能し、簡単な例としては良いオプションかもしれませんが、推奨されるアプローチはMockWebServerを使用することです。

いつものように、この記事のソースコードはGitHubで入手できます。