はじめに
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
実行方法
-
yasuflatland-lf/go-cloudrun-boilerplateを
git clone
する。 -
go mod tidy
でモジュール群をロード。 -
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でも同じものがないかなと探していて見つけました。近年はある言語で使えるライブラリやツールが、他の言語にもポートされている、というようなことが多くあるように感じます。