はじめに
こんにちは!
エンジニア2年目のTKDSです!
前回はDaggerを紹介しました。
今回もコンテナ技術を活用して、テストを容易にするツールについて紹介します。
今回取り上げるのは、統合テストやエンドツーエンドテストのためにDockerコンテナを利用するライブラリ、Testcontainersです。
Testcontainersとは
Testcontainersはさまざまなプログラミング言語(Java、Go、Python、Node.jsなど)向けに提供されており、Daggerと同様にテスト用のコンテナを簡単に作成することができます。
前回の記事で紹介したユースケース2と同様のことが実現できるため、テスト内にコンテナの起動コードを直接書くことが可能です。
Testcontainersを使用することでモックを使用せずに外部サービスに依存するテストを書くことが可能になります。
Testcontainersのメリット
次にTestcontainersのメリットについて紹介します。
モックを使わずにテストをかける
前述のようにコンテナでDBなどの外部サービスを用意することでモックに依存しないテストが書けます。
コンテナで用意できる対象は、DBに限らず、MQやNoSQL、Key-value storeなど多岐に渡ります。
事前に用意されているモジュールやコンテナイメージを直接指定してコンテナを作ることができます。
2つ目の方法については後ほど詳しく説明します。事前に用意されたモジュールがある
前項目で述べたようにTestcontainers moduleが用意されているため、面倒な準備なしに様々なミドルウェアが使用可能です。ローカル環境と CI 環境の両方で一貫した環境の用意が可能
コンテナで環境が用意できるため、ローカルでは実行できるのにCIではテストが落ちる、またはその逆など環境依存のテストの不安定さを取り除けます。
参考
- https://testcontainers.com/getting-started/
- https://testcontainers.com/guides/
- https://testcontainers.com/
ハンズオン
では、実際にTestcontainersを使ってみます。
今回はGoを使います。
環境設定
筆者の環境
shun@shun-ThinkPad-P14s-Gen-4:~$ cat /etc/os-release PRETTY_NAME="Ubuntu 22.04.4 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.4 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy shun@shun-ThinkPad-P14s-Gen-4:~$ docker version Client: Docker Engine - Community Version: 26.1.3 API version: 1.45 Go version: go1.21.10 Git commit: b72abbb Built: Thu May 16 08:33:29 2024 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 26.1.3 API version: 1.45 (minimum version 1.24) Go version: go1.21.10 Git commit: 8e96db1 Built: Thu May 16 08:33:29 2024 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.32 GitCommit: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89 runc: Version: 1.1.12 GitCommit: v1.1.12-0-g51d5e94 docker-init: Version: 0.19.0 GitCommit: de40ad0
事前にDockerのインストールは済ませておいてください。
goプロジェクトの作成
mkdir go-testcontainers cd go-testcontainers go mod init go-testcontainers
必要なパッケージのインストール
go get github.com/testcontainers/testcontainers-go go get github.com/testcontainers/testcontainers-go/modules/postgres go get github.com/jackc/pgx/v4
テストコードの作成
コード全体はgithubにおいてあります。
Testcontainersに関わる部分だけ説明します。
今回は事前に用意されたモジュールを使わずにDBを用意します。
コンテナリクエストの設定
この部分では、コンテナを起動するためのリクエストを設定しています。
req := testcontainers.ContainerRequest{ Image: "postgres:latest", ExposedPorts: []string{"5432/tcp"}, Env: map[string]string{ "POSTGRES_DB": "testdb", "POSTGRES_USER": "testuser", "POSTGRES_PASSWORD": "testpassword", }, WaitingFor: wait.ForListeningPort("5432/tcp"), }
Image
: 使用するDockerイメージを指定します。ここではpostgres:latestを指定しており、最新のPostgreSQLイメージを使用します。ExposedPorts
: コンテナの公開ポートを指定します。PostgreSQLはデフォルトで5432ポートを使用するため、5432/tcpを指定しています。Env
: コンテナ内の環境変数を設定します。ここではデータベース名、ユーザー名、パスワードを設定しています。WaitingFor
: コンテナが準備完了になるまで待機する条件を指定します。ここでは、5432ポートがリスニング状態になるのを待ちます。
参考
- https://golang.testcontainers.org/quickstart/
- https://github.com/testcontainers/testcontainers-go/blob/v0.31.0/container.go#L123
コンテナの起動
ここでは、設定したリクエストに基づいてコンテナを起動しています。
postgresContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) if err != nil { t.Fatal(err) } defer postgresContainer.Terminate(ctx)
testcontainers.GenericContainer
: 汎用的なコンテナを起動するための関数です。ctx
: コンテキストを渡します。これは操作のキャンセルやタイムアウトを管理するために使用されます。ContainerRequest
: 先ほど設定したコンテナリクエストを渡します。Started: true
: コンテナがすぐに起動するように指定します。
コンテナのホストとポートの取得
ここでは、起動したコンテナのホストとマッピングされたポートを取得しています。
host, err := postgresContainer.Host(ctx) if err != nil { t.Fatal(err) } port, err := postgresContainer.MappedPort(ctx, "5432") if err != nil { t.Fatal(err) }
postgresContainer.Host(ctx)
: コンテナのホスト名を取得します。通常はlocalhostになります。postgresContainer.MappedPort(ctx, "5432")
: コンテナ内の5432ポートがホストのどのポートにマッピングされているかを取得します。
結果の確認
実際にテストを実行し、結果を確認してみましょう。
go mod tidy go test -v ./…
無事テストが実行されたことが確認できました!
画像のように、通常のgo testで実行することができ、非常に簡単にDB依存のテストが実行できます。
まとめ
今回はTestcontainersについて紹介しました。
今まではモジュールを使った方法しか知らなかったため、今回のハンズオンで触れてみた内容は新たな学びになりました。
自分でやりたいことを設定できるのであれば、Daggerの代わりに使ってみるのもありかもしれません。
特に、コンテナを用意するだけでCI/CDパイプラインを書かない・Goを使用しない等であれば有力な選択肢になりそうです。
ここまで読んでいただきありがとうございました。