Go

GolangでDBアクセスがあるユニットテストのやり方を考える - Qiita

func skipCI(t *testing.T) {
  if os.Getenv("CI") != "" {
    t.Skip("Skipping testing in CI environment")
  }
}
 
func TestNewFeature(t *testing.T) {
  skipCI(t)
}

あるいはshortではスキップにする go test -short

if testing.Short() {
  t.Skip("skipping testing in short mode")
}

dockertestとfixtureを使ったテスト

dockertest を使ってコンテナを起動し、testfixtures でテストデータを流し込む

package db_test
 
import (
	"database/sql"
	"fmt"
	"os"
	"testing"
	"time"
 
	"github.com/go-testfixtures/testfixtures/v3"
	"github.com/ory/dockertest/v3"
)
 
var (
	pool     *dockertest.Pool
	resource *dockertest.Resource
	db       *sql.DB
	fixtures *testfixtures.Loader
)
 
func prepareTestDatabase() error {
	var err error
 
	pool, err = dockertest.NewPool("")
	// コンテナの起動に時間がかかるため
	pool.MaxWait = 2 * time.Minute
	if err != nil {
		return err
	}
 
	basepath, _ := os.Getwd()
 
	user := "user"
	// DB名に test が入っていないと、fixtureのload時にエラーになる (https://github.com/go-testfixtures/testfixtures#security-check)
	dbName := "test_db"
 
	// Dockerコンテナ起動時の細かいオプションを指定する
	runOptions := &dockertest.RunOptions{
		Repository: "mysql",
		Tag:        "5.7",
		Env: []string{
			fmt.Sprintf("MYSQL_DATABASE=%s", dbName),
			fmt.Sprintf("MYSQL_USER=%s", user),
			fmt.Sprintf("MYSQL_PASSWORD=%s", user),
			"MYSQL_ROOT_PASSWORD=secret",
			"TZ=Asia/Tokyo",
		},
		Mounts: []string{
			basepath + "/etc/01_create_table.sql:/docker-entrypoint-initdb.d/01_create_table.sql",
		},
		Cmd: []string{
			"mysqld",
			"--character-set-server=utf8",
			"--collation-server=utf8_unicode_ci",
		},
	}
 
	// imageをpullして起動する
	resource, err = pool.RunWithOptions(runOptions)
	if err != nil {
		return err
	}
 
	// コンテナが起動されたことを確認するためのコマンドを定義
	if err := pool.Retry(func() error {
		time.Sleep(10 * time.Second)
		err := connectDb(resource.GetPort("3306/tcp"))
		if err != nil {
			return err
		}
		return db.Ping()
	}); err != nil {
		return err
	}
 
	return nil
}
 
func connectDb(port string) error {
	dsn := fmt.Sprintf("%s:%s@tcp(localhost:%s)/%s?parseTime=true&loc=Asia2FTokyo",
		user,
		user,
		port,
		dbName,
	)
	dbURL := func(port nat.Port) string {
		return dsn
	}
	execError := compose.
		WaitForService("db", wait.ForSQL(nat.Port(port), "mysql", dbURL)).
		WithCommand([]string{"up", "-d"}).
		Invoke()
	err := execError.Error
	if err != nil {
		return fmt.Errorf("Could not run compose file: %v - %v", composeFilePaths, err)
	}
 
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	return db.Ping()
}
 
func TestSample(t *testing.T) {
	// テストデータ準備 start
	if testing.Short() {
		t.Skip("skip integration test")
	}
	var err error
	err = prepareContainer(t)
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := compose.Down().Error; err != nil {
			t.Fatalf("Could not purge resource: %v", err)
		}
	}()
	// テストデータ準備 end
 
	// ... test
}