skip to content
cruzluna.dev
Go Docker Cover Image

Setting Up Integration Tests with Docker & Go

/ 2 min read

Tldr

I will go through how I spin up a docker container to set up some simple integration tests to ensure my queries actually work, without having to resort to refreshing the Neon DB console.

Using some markdown elements

Helper function to spin up a test database in a Docker container.

func StartTestDatabase(tb testing.TB) {
	tb.Helper()

	dsn := &url.URL{
		Scheme: "postgres",
		User:   url.UserPassword("username", "password"),
		Path:   "neondb",
	}

	q := dsn.Query()
	q.Add("sslmode", "disable")
	dsn.RawQuery = q.Encode()

	pool, err := dockertest.NewPool("")
	if err != nil {
		tb.Fatalf("Could not connect to Docker: %s", err)
	}

	// docker + pool is configured at this point
	pw, _ := dsn.User.Password()
	env := []string{
		fmt.Sprintf("POSTGRES_USER=%s", dsn.User.Username()),
		fmt.Sprintf("POSTGRES_PASSWORD=%s", pw),
		fmt.Sprintf("POSTGRES_DB=%s", dsn.Path),
	}

	resource, err := pool.Run("postgres", "13-alpine", env)
	if err != nil {
		tb.Fatalf("Could not start postgres container: %v", err)
	}
	tb.Cleanup(func() {
		err = pool.Purge(resource)
		if err != nil {
			tb.Fatalf("Could not purge container: %v", err)
		}
	})
	// ----------------

	_ = resource.Expire(60)

	// MacOS specific
	if runtime.GOOS == "darwin" {
		dsn.Host = net.JoinHostPort(resource.GetBoundIP("5432/tcp"), resource.GetPort("5432/tcp"))
	}

	ctx := context.Background()

	var db *pgx.Conn
	// gets around DB reconnect fail
	// https://github.com/lib/pq/issues/835
	for i := 0; i < 20; i++ {
		db, err = pgx.Connect(ctx, dsn.String())
		if err == nil {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	if db == nil {
		tb.Fatalf("Couldn't connect to database: %s", err)
	}

	defer db.Close(ctx)

	if err = pool.Retry(func() (err error) {
		return db.Ping(ctx)
	}); err != nil {
		tb.Fatalf("Couldn't ping DB: %s", err)
	}

	// migrate table in docker
	m, err := migrate.New(
		"file://migrations",
		dsn.String())
	if err != nil {
		tb.Fatal(err)
	}
	if err := m.Up(); err != nil {
		tb.Fatal(err)
	}

  // unique to my initialization
	err = Init(ctx, dsn.String())

	if err != nil {
		tb.Fatal("Unable to initialize singleton db connection: ", err)
	}
}