コンテンツにスキップ

Java API テスト

API テストは HTTP 経由でアプリを検証します。@ShadowPg が pgmem の起動、アプリの接続設定、テストごとのデータ復元を処理します。既存の DataSource と接続プールはテスト中も維持されます。

HTTP サーバーをランダムなポートで起動する

Section titled “HTTP サーバーをランダムなポートで起動する”

以下はアプリに POST /orders と GET /orders がある想定です。db/schema.sql は src/test/resources に配置します。

OrdersApiTest.java
@ShadowPg(schema = "db/schema.sql")
class OrdersApiTest {
@LocalServerPort int port;
HttpClient http = HttpClient.newHttpClient();
@Test void createsOrderThroughHttp() throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create(baseUrl() + "/orders"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"sku\":\"book\"}"))
.build();
assertEquals(201, http.send(request, HttpResponse.BodyHandlers.ofString()).statusCode());
}
@Test void startsFromPreparedDataAgain() throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create(baseUrl() + "/orders")).build();
var response = http.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("[]", response.body());
}
String baseUrl() { return "http://127.0.0.1:" + port; }
}

アプリの通常のテスト用依存に加え、Spring Boot は testImplementation("io.github.shibukawa.pgmem:pgmem-spring:1.18.0")、Micronaut は testImplementation("io.github.shibukawa.pgmem:pgmem-micronaut:1.18.0") を追加します。それぞれ io.github.shibukawa.pgmem.spring.ShadowPg または io.github.shibukawa.pgmem.micronaut.ShadowPg をインポートします。このアノテーションがアプリの標準の DataSource 設定に pgmem の URL を渡し、アプリ起動後の状態を復元基準にします。起動時のマイグレーションを使う場合は schema を省略できます。追加の初期データは seed = "db/seed.sql" で指定します。クラスごとに別のフォークとサーバーを持ちます。

PostgreSQL JDBC ドライバーとプラットフォーム用の pgmem-native も、Java の基本設定に従ってテスト依存へ追加します。

DataSource を直接受け取るフレームワークなら、リクエスト受付前に pg.applicationFork().dataSource() でアプリを構築します。別の JVM で動かす場合は、プールを作る前の起動設定へ pg.applicationFork().jdbcUrl() を渡します。同じアプリとフォークを逐次ケースで維持し、@BeforeEach で reset() を呼びます。

スイート固有の基準データが必要なら、アプリ用フォークに一度シードを入れ、Snapshot seeded = pg.applicationFork().snapshot() を作ります。各ケースの前に pg.applicationFork().reset(seeded) で戻し、スイート終了後に seeded を閉じます。

同じ HTTP ライフサイクルをブラウザーで試す場合は E2E テスト を参照してください。

1 つの @ShadowPg Spring Boot・Micronaut アプリを使うメソッドは逐次実行します。HTTP サーバー、Pool、フォークを共有し、各メソッドの前に同じフォークを reset するためです。異なるクラスは、それぞれ別のアプリコンテキスト、pgmem フォーク、HTTP ポートを持つ場合に並列化します。src/test/resources/junit-platform.properties ではクラスだけを並列化できます。

junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = same_thread
junit.jupiter.execution.parallel.mode.classes.default = concurrent

static なアプリや DataSource をクラス間で共有しないでください。次の reset 前にリクエストと DB を使うバックグラウンド処理を終え、トランザクションを閉じます。全メソッドを並列化するなら、クラス単位のアノテーション方式ではなくメソッドごとに別のアプリとフォークを作ります。