Querier

dockertestを使ってDockerコンテナ内でテスト用のデータベースを立ち上げ、GitHub ActionsでCIを回す

2023.02.10に公開 | 2023.02.10に更新

Querier運営

@querier_io@querierinc

「Querier(クエリア)」は社内向け管理画面を圧倒的な速さで、かつビジネスのスケールに合わせて柔軟に構築することができるローコードツールです。

管理画面の構築もWeb上で完結
エンジニアのためのローコードツール

Querierについて詳しく見る

こんにちは、クエリアの吉田です。
裏側にデータベースを持つAPIサーバーのテストを実際のDBを使ってテストするとき、データベースをどのように用意するか迷ったことはありませんか?
そんな悩みを抱えている方に今回は、dockertestというツールを紹介します。dockertestとは、インテグレーションテストのために作られた、手軽にDockerコンテナを立ち上げることができるGo製のツールです。
クエリアではMySQLを使っているので、dockertestを使ってMySQL8.0のイメージをベースにコンテナを立ち上げ、DBコネクションを確立する方法を紹介したいと思います。

なぜdockertestなのか

Goでテストを書くときに、データベース層をモックにしながら、ユニットテストを書くことはあると思いますが、実際にレコードに反映されているかまでは担保することはできません。特にスタートアップで最小限の工数で安全性を担保したい場合には、実際のデータベースを使ったテストのみを書くほうが、工数対効果が高いと思っています。

しかし、実際にテスト用にデータベースを用意したり、ローカル環境に依存するようにテスト環境を構築してしまうと、CIで回しづらくなったり、各ローカル環境に依存してしまうなどの問題が発生します。

そういった問題をすべて解決してくれるのが、dockertestです。今回は、実際にdockertestを使ってどのようにテストを記述しているのか、GitHub Actionsを使ってCIを回すところまで解説していきます。

dockertestの使い方

まずは、以下のコマンドでdockertestをインストールします。

$ go get -u github.com/ory/dockertest/v3


これで準備は完了したので、実際にテストを書いていきます。
今回は testutil パッケージ内にコンテナを立ち上げて、DBとのコネクションを確立するまでの汎用的な関数を作成します。

package testutil

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/ory/dockertest/v3"
	"github.com/ory/dockertest/v3/docker"
)

var db *sql.DB

func SetupContainer() func() error {
	pool, err := dockertest.NewPool("")
	pool.MaxWait = time.Minute * 2
	if err != nil {
		log.Fatalf("Could not connect to docker: %s", err)
	}

	runOptions := &dockertest.RunOptions{
		Repository: "mysql",
		Tag:        "8.0",
		Env: []string{
			"MYSQL_DATABASE=mydb",
			"MYSQL_PASSWORD=password",
			"MYSQL_ROOT_PASSWORD=password",
			"TZ=Asia/Tokyo",
		},
		Cmd: []string{
			"mysqld",
			"--character-set-server=utf8mb4",
			"--collation-server=utf8mb4_unicode_ci",
		},
	}
	hcOptions := func(config *docker.HostConfig) {
		config.AutoRemove = true
	}

	resource, err := pool.RunWithOptions(runOptions, hcOptions)
	if err != nil {
		log.Fatalf("Could not start resource: %s", err)
	}

	if err := pool.Retry(func() error {
		time.Sleep(time.Second * 10)

		var err error
		db, err = sql.Open("mysql", fmt.Sprintf("root:password@(0.0.0.0:%s)/mydb?parseTime=true", resource.GetPort("3306/tcp")))
		if err != nil {
			return err
		}

		return db.Ping()
	}); err != nil {
		log.Fatalf("Could not connect to docker: %s", err)
	}

	return func() error { return pool.Purge(resource) }
}

func GetDB() *sql.DB {
	return db
}

上の関数ができたら、 main_test.goSetupContainer() を呼び出し、DBコネクションを使いたい場所で GetDB() することで、データベースとのコネクションを取得することができます。

func TestMain(m *testing.M) {
	purge := testutil.SetupContainer()

	c := m.Run()

	if err := purge(); err != nil {
		log.Fatalf("Could not purge resource: %s", err)
	}

	os.Exit(c)
}

GitHub ActionsでCIを回す

GitHub ActionsでCIを実行するのも、難しいことは一切なく、以下の設定ファイルを用意するだけです。

name: test

on: [pull_request]

jobs:
  test:
    strategy:
      matrix:
        go-version: [1.19.x]
        os: [ubuntu-latest]
    name: test
    runs-on: ${{ matrix.os }}
    timeout-minutes: 10
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup Go
        uses: actions/setup-go@v2
        with:
          go-version: ${{ matrix.go-version }}
      - name: E2E test
        run: go test -v -cover -covermode=count ./e2e/


最後に

今回は、dockertestを使ってテスト用のコンテナを立ち上げる方法を紹介しました。テストは如何にストレスなく書ける環境を作れるかが勝負だと思っています。
実際のデータベースを使ってテストを書きたいと思っている方はぜひ試してみてください。

また、クエリアは様々なデータベースやAPIと連携して、社内向けの管理画面やツールを構築できるローコードツールの『Querier』を開発しています。無料トライアルも実施しておりますので、もし気になる方がいればサイトを覗いてみてください。

「Querier(クエリア)」は社内向け管理画面を圧倒的な速さで、かつビジネスのスケールに合わせて柔軟に構築することができるローコードツールです。

最新の記事

【告知】値の参照時の仕様変更のお知らせ

このたび2024年11月11日に値の参照に関する仕様変更を予定しておりますので詳細について報告いたします。

more

管理画面の構築もWeb上で完結
エンジニアのためのローコードツール

Querierについて詳しく見る