4
6

More than 1 year has passed since last update.

Goでテスト用Dockerコンテナ(MySQL, Redis)などを動かす

Last updated at Posted at 2021-03-16

はじめに

Goで書いたアプリケーションのテスト時に、データベースへの接続や、キャッシュへの接続などが必要になることが多いかと思います。その際に手軽にテスト用データベースやキャッシュなどをを立ち上げられるtestcontainer-goというツールを利用してみて、非常に簡単に利用することができました。どなたかのお役に立てたら幸いです。

動作するコードは以下のリポジトリに置いてあります。
yasuflatland-lf/go-cloudrun-boilerplate

動作環境

  • Mac 11.2.3 (BigSur)
  • Go 1.15.8
  • Docker Desktop for Mac (3.2.1)
  • Git 2.23.0

実行方法

  1. yasuflatland-lf/go-cloudrun-boilerplategit cloneする。
  2. go mod tidyでモジュール群をロード。
  3. go testを実行

解説

テストコンテナを呼び出しているのがこのファンクションの中になります。

func initMySQLContainer() (string, func()) {
	ctx := context.Background()
	username := "root"
	password := "password"
	seedDataPath, err := os.Getwd()
	mysqlPort, _ := nat.NewPort("tcp", "3306")
	req := testcontainers.ContainerRequest{
		Image:        "mysql:5.7",
		ExposedPorts: []string{"3306/tcp"},
		Env: map[string]string{
			"MYSQL_ROOT_PASSWORD": "password",
		},
		BindMounts: map[string]string{
			seedDataPath + "/test/db/mysql_init": "/docker-entrypoint-initdb.d",
			seedDataPath + "/test/db/my.cnf":     "/etc/mysql/conf.d/my.cnf",
		},
		WaitingFor: wait.ForListeningPort(mysqlPort),
	}
	mysqlC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
		ContainerRequest: req,
		Started:          true,
	})
	if err != nil {
		panic(err)
	}

	ip, err := mysqlC.Host(ctx)
	if err != nil {
		panic(err)
	}

	port, err := mysqlC.MappedPort(ctx, "3306")
	if err != nil {
		panic(err)
	}

	// cloudSQL service fetch MySQL connection data from environment valuables.
	// Set here dummy server information for test purpose.
	os.Setenv("DB_NAME", "test")
	os.Setenv("DB_USERNAME", username)
	os.Setenv("DB_PASSWORD", password)
	os.Setenv("DB_IP", ip)
	os.Setenv("DB_PORT", port.Port())

	dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%d)/test", username, password, ip, port.Int())
	fmt.Println(dataSourceName)
	cTerm := func() {
		defer mysqlC.Terminate(ctx)
	}

	return dataSourceName, cTerm
}

testcontainer-go公式を見てもらうとわかるように、MySQLだけでなくさまざまなコンテナを起動することができます。大体上記コードを見てもらうと、どこにパスワードを設定して、みたいな部分はわかると思いますが、いくつかポイントを説明してみます。

初期データのロードとMySQLの初期設定

以下の部分でコンテナのファイルやディレクトリへマウントを行っています。

BindMounts: map[string]string{
    seedDataPath + "/test/db/mysql_init": "/docker-entrypoint-initdb.d",
    seedDataPath + "/test/db/my.cnf":     "/etc/mysql/conf.d/my.cnf",
},

/test/db/mysql_init

初期データのロード(データベース作成、テーブル作成、レコード挿入)をしています。MySQLのコンテナの説明を読むと、/docker-entrypoint-initdb.dの下に配置されたファイルを初期化時に読み取る、ということで、ここに初期化設定ファイルを置いています。

/test/db/my.cnf

MySQLの設定ファイルをマウントしています。ここにMySQLの設定を書きます。筆者のテスト環境ではutf8mb4の設定が必要(絵文字などを正しく表示するにはこのキャラクタセットが必要)だったので、その設定が書かれています。

IP、Portデータの取得

testcontainer-goのインスタンスを起動すると、コンテナは3306で起動しますが、ホスト側にランダムなポートを渡してきます。それを取得して、今回は環境変数に入れて、それをデータベースアクセス側のファンクションで取り出して利用するというようにしています。

ip, err := mysqlC.Host(ctx)
if err != nil {
    panic(err)
}

port, err := mysqlC.MappedPort(ctx, "3306")
if err != nil {
    panic(err)
}

あとがき

もともとJavaのアプリケーション作成時にtestcontainerを利用していて、Goでも同じものがないかなと探していて見つけました。近年はある言語で使えるライブラリやツールが、他の言語にもポートされている、というようなことが多くあるように感じます。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6