【Java】HTTP通信でHttpClientを使用してみるよ!JUnitでテストも実装してみるぜ!

Java 11 の HttpClient を使って外部システムに POST リクエストを送信する API クライアントを Gradle プロジェクトとして構築する場合、以下のステップで進めることができます

1. Gradle プロジェクトのセットアップ

build.gradleを設定します

HttpClient は Java 11 に組み込まれているため、追加の依存関係は不要ですが、依存関係として必要なライブラリがある場合はここで追加します

plugins {
    id 'java'
}

group 'com.example'
version '1.0-SNAPSHOT'

sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    // 必要に応じて他のライブラリを追加
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

test {
    useJUnitPlatform()
}

2. APIクライアントの作成

次に、実際の HTTP 通信を行うクライアントクラスを作成します

今回はpost処理を実装しますね

package com.example.api.client;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class HttpClientService {

    private final HttpClient httpClient;

    public HttpClientService() {
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    }

    public String sendPostRequest(String uri, String jsonPayload) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(new URI(uri))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(jsonPayload, StandardCharsets.UTF_8))
            .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() == 200) {
            return response.body();
        } else {
            throw new RuntimeException("Failed : HTTP error code : " + response.statusCode());
        }
    }
}

こんな感じです(笑)

3. テストの追加

JUnit を使用してこのクライアントをテストする場合、モックを利用して外部システムに依存しないテストを書くと良いですね
(実際につなげてしまうと良くないことが起こりうる。。。)

その場合、Mockito などのモックライブラリを使用します!

送信されるリクエストの URL、リクエストボディ、ヘッダーなども検証していきます

package com.example.api.client;

import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

class HttpClientServiceTest {

    @Test
    void testSendPostRequest() throws Exception {
        // モックの準備
        HttpClient mockClient = mock(HttpClient.class);
        HttpResponse<String> mockResponse = mock(HttpResponse.class);

        // モックの戻り値設定
        when(mockResponse.statusCode()).thenReturn(200);
        when(mockResponse.body()).thenReturn("Success");
        when(mockClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class)))
            .thenReturn(mockResponse);

        // テスト対象のサービスをモック HttpClient で初期化
        HttpClientService clientService = new HttpClientService(mockClient);

        String url = "https://api.example.com/resource";
        String jsonPayload = "{\"key\": \"value\"}";

        // 実際にメソッドを呼び出す
        String response = clientService.sendPostRequest(url, jsonPayload);

        // 戻り値の確認
        assertEquals("Success", response);

        // ArgumentCaptor を使って送信されたリクエストをキャプチャ
        ArgumentCaptor<HttpRequest> requestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
        verify(mockClient).send(requestCaptor.capture(), any(HttpResponse.BodyHandler.class));

        // キャプチャしたリクエストを検証
        HttpRequest capturedRequest = requestCaptor.getValue();

        // 送信されたリクエストの URL を確認
        assertEquals(URI.create(url), capturedRequest.uri());

        // 送信されたリクエストのボディを確認
        assertTrue(capturedRequest.bodyPublisher().isPresent());
        HttpRequest.BodyPublisher bodyPublisher = capturedRequest.bodyPublisher().get();
        assertEquals(jsonPayload, new String(bodyPublisher.contentLength() > 0
            ? bodyPublisher.toString() : ""));

        // 送信されたリクエストのヘッダーを確認
        assertEquals("application/json", capturedRequest.headers().firstValue("Content-Type").orElse(""));
    }
}

ポイント

  1. ArgumentCaptor を使用して、HttpRequest オブジェクトをキャプチャし、モックが呼び出されたときに実際に送信されたリクエストの内容を取得します
  2. verify() を使って、HttpClient.send() メソッドが呼び出されたことを確認します
  3. キャプチャしたリクエストの検証: キャプチャしたリクエストから URL、ヘッダー、ボディを取得し、予想される値と一致するかどうかを検証します

注意点

  • HttpRequest の BodyPublisher はストリームとしてデータを送信するため、テスト中では単純に文字列として扱うのが難しいです。。。
  • Mockito を使うことで、テスト対象が外部システムに依存せずにテストできるため、安定したテストを実現できます

この方法で、送信されるリクエストの中身 (URL、ヘッダー、ボディ) を確認できるテストを実行できます

参考程度に…

BodyPublisher から文字列を取るには以下を参考にすると良いかもしれないです
参考程度にご覧ください

    // リクエストボディの内容を確認するための実装
    HttpRequest.BodyPublisher bodyPublisher = capturedRequest.bodyPublisher().orElseThrow();

    // ByteArray を使ってボディの内容をキャプチャ
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    bodyPublisher.subscribe(new Flow.Subscriber<ByteBuffer>() {
        @Override
        public void onSubscribe(Flow.Subscription subscription) {
            subscription.request(Long.MAX_VALUE);
        }

        @Override
        public void onNext(ByteBuffer byteBuffer) {
            byte[] bytes = new byte[byteBuffer.remaining()];
            byteBuffer.get(bytes);
            outputStream.write(bytes, 0, bytes.length);
        }

        @Override
        public void onError(Throwable throwable) {}

        @Override
        public void onComplete() {}
    });

    // キャプチャされたボディの内容を文字列として取得
    String capturedBody = outputStream.toString(StandardCharsets.UTF_8);

    // ボディが期待通りの JSON かを確認
    assertEquals(jsonPayload, capturedBody);

是非参考にしてみてください!

是非フォローしてください

最新の情報をお伝えします