🙆

Testcontainers使ってみた

2024/11/12に公開

Testcontainersとは

https://testcontainers.com/

Testcontainersは、データベース、メッセージ・ブローカー、ウェブ・ブラウザなど、Dockerコンテナで動作するあらゆるものの軽量インスタンスを提供するためのオープンソース・ライブラリです。

テストの依存関係をコードとして定義し、テストを実行するだけで、コンテナが作成され、削除されます。
モックや複雑な環境設定はもう必要ありません。

多くの言語とテストフレームワークをサポートしており、必要なのはDockerだけです。

背景

2023年12月、Docker社がTestcontainersの開発元であるAtomicJar社を買収したことが発表されました。
これにより、Testcontainersの開発はさらに活発になることが予想されます。
今まで、GoのDBのテストにはdockertestを使っていましたが、この機会にTestcontainersを使ってみることにしました。
https://x.com/testcontainers/status/1734245890515140699

サンプルコード

以下のコードは、Testcontainers for Goで、PostgreSQLのデータベースを使用してテストを行うサンプルです。

package test

import (
	"context"
	"database/sql"
	"log"
	"path/filepath"
	"testing"
	"time"

	"github.com/testcontainers/testcontainers-go"
	"github.com/testcontainers/testcontainers-go/modules/postgres"
	"github.com/testcontainers/testcontainers-go/wait"

	_ "github.com/lib/pq"
)

var db *sql.DB // データベース接続用のグローバル変数

// テスト全体のセットアップとクリーンアップを管理するエントリポイント
func TestMain(m *testing.M) {
	ctx := context.Background()

	// データベースの設定情報
	dbName := "users"
	dbUser := "user"
	dbPassword := "password"

	// PostgreSQLコンテナを起動
	postgresContainer, err := postgres.Run(ctx,
		"postgres:16-alpine", // 使用するPostgreSQLイメージ
		postgres.WithInitScripts(filepath.Join("testdata", "init.sql")), // 初期化スクリプトを指定
		postgres.WithDatabase(dbName),                                   // データベース名を指定
		postgres.WithUsername(dbUser),                                   // ユーザー名を指定
		postgres.WithPassword(dbPassword),                               // パスワードを指定
		testcontainers.WithWaitStrategy( // コンテナの起動完了を待つための戦略
			wait.ForLog("database system is ready to accept connections"). // 特定のログ出力を待機
											WithOccurrence(2).                  // ログが2回出現するまで待つ
											WithStartupTimeout(5*time.Second)), // タイムアウトを設定
	)
	defer func() {
		// テスト終了後にコンテナを停止
		if err := testcontainers.TerminateContainer(postgresContainer); err != nil {
			log.Fatalf("failed to terminate container: %s", err)
		}
	}()
	if err != nil {
		log.Fatalf("failed to start container: %s", err)
	}

	// PostgreSQLへの接続文字列を取得
	dbURL, err := postgresContainer.ConnectionString(ctx, "sslmode=disable")
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	// データベース接続を初期化
	db, err = sql.Open("postgres", dbURL)
	if err != nil {
		log.Fatalf("error: %s", err)
	}
	defer db.Close() // テスト終了時に接続を閉じる

	// テストを実行
	m.Run()
}

// 実際のテストケース(例)
func TestXXX(t *testing.T) {
	// ここにテストケースを記述
}

感想

dockertestには、Testcontainersのようなpostgres.WithInitScriptsに相当する初期化スクリプトを実行する仕組みがないので、Testcontainersは便利だなと思いました。

Discussion