Testcontainers를 사용한 Go 통합 테스트

귀하의 애플리케이션은 PostgreSQL과 같은 데이터베이스를 사용합니까? 그렇다면 실제PostgreSQL 데이터베이스에서 제대로 작동하는지 확인하기 위해 지속성 계층을 어떻게 테스트합니까? 맞습니다. 실제PostgreSQL에 대해 테스트해야 합니다. 해당 테스트에는 외부 인프라가 필요하므로 통합 테스트입니다. GoTestcontainers 을 사용하여 Docker 애플리케이션에 대한 통합 테스트를 작성하는 것이 얼마나 쉬운지 배우게 됩니다.

통합 테스트 설정



우리의 응용 프로그램은 PostgreSQL 데이터베이스에 사용자를 저장합니다. 일반 SQL을 사용하여 사용자 이름으로 사용자를 찾는 메서드UserRepository와 함께 구조체FindByUsername를 사용합니다. 메서드 FindByUsername에 대해 PostgreSQL의 실제 Docker에 대해 실행되는 통합 테스트를 작성합니다.
FindByUsernameUserRepository에 대한 통합 테스트는 다음과 같습니다.

func TestUserRepository(t *testing.T) {
    // Setup database
    dbContainer, connPool := SetupTestDatabase()
    defer dbContainer.Terminate(context.Background())

    // Create user repository
    repository := NewUserRepository(connPool)

    // Run tests against db
    t.Run("FindExistingUserByUsername", func(t *testing.T) {
        adminUser, err := repository.FindByUsername(
            context.Background(),
            "admin",
        )

        is.NoErr(err)
        is.Equal(adminUser.Username, "admin")
    })
}

먼저 데이터베이스가 설정됩니다. 그런 다음 데이터베이스UserRepository의 연결 풀에 대한 참조를 사용하여 테스트를 위해 새connPool가 생성됩니다. 아니요 테스트할 메서드userRepository.FindByUsername(ctx, "admin")를 실행하고 결과를 확인합니다. 하지만 잠깐, 그 데이터베이스 컨테이너는 어디에서 왔습니까? 맞습니다. TestcontainersDocker 을 사용하여 설정하겠습니다.

Testcontainers를 사용한 데이터베이스 설정



PostgreSQL 라이브러리를 사용하여 Docker 컨테이너에 Testcontainers 데이터베이스를 설정합니다.

첫 번째 단계로 노출된 포트testcontainers.ContainerRequest를 사용하여 Docker 이미지를 postgres:14로 설정하는 5432/tcp를 생성합니다. 데이터베이스 이름과 사용자 이름 및 비밀번호는 환경 변수를 사용하여 설정됩니다. 그리고 데이터베이스 컨테이너가 실행 중일 때만 테스트가 시작되도록 하기 위해 WaitingFor 옵션과 함께 wait.ForListeningPort("5432/tcp") 옵션을 사용하여 테스트를 기다립니다.

이제 두 번째 단계로 요청된 컨테이너를 시작합니다.

마지막으로 3단계에서는 fmt.Sprintf("postgres://postgres:postgres@%v:%v/testdb", host, port.Port())가 있는 데이터베이스의 연결 문자열에서 실행 중인 데이터베이스 컨테이너의 호스트와 포트를 사용합니다. 이제 pgxpool.Connect(context.Background(), dbURI) 와 연결합니다.

PostgreSQL 컨테이너를 설정하는 전체 방법SetupTestDatabase은 다음과 같습니다(오류 생략됨).

func SetupTestDatabase() (testcontainers.Container, *pgxpool.Pool) {
    // 1. Create PostgreSQL container request
    containerReq := testcontainers.ContainerRequest{
        Image:        "postgres:latest",
        ExposedPorts: []string{"5432/tcp"},
        WaitingFor:   wait.ForListeningPort("5432/tcp"),
        Env: map[string]string{
            "POSTGRES_DB":       "testdb",
            "POSTGRES_PASSWORD": "postgres",
            "POSTGRES_USER":     "postgres",
        },
    }

    // 2. Start PostgreSQL container
    dbContainer, _ := testcontainers.GenericContainer(
        context.Background(),
        testcontainers.GenericContainerRequest{
            ContainerRequest: containerReq,
            Started:          true,
    })

    // 3.1 Get host and port of PostgreSQL container
    host, _ := dbContainer.Host(context.Background())
    port, _ := dbContainer.MappedPort(context.Background(), "5432")

    // 3.2 Create db connection string and connect
    dbURI := fmt.Sprintf("postgres://postgres:postgres@%v:%v/testdb", host, port.Port())
    connPool, _ := pgxpool.Connect(context.Background(), dbURI)

    return dbContainer, connPool
}
defer dbContainer.Terminate(context.Background())와의 통합 테스트 후에 PostgreSQL 컨테이너가 종료되었는지 확인합니다.

데이터베이스 마이그레이션 추가



지금까지 테스트는 빈 데이터베이스로 시작되었습니다. 응용 프로그램의 데이터베이스 테이블이 필요하기 때문에 그다지 유용하지 않습니다. 이 예에서는 users 테이블이 필요합니다. 이제 golang-migrate을 사용하여 데이터베이스를 설정합니다.
SetupTestDatabase() 호출을 추가하여 MigrateDb(dbURI) 메서드에 데이터베이스 마이그레이션을 추가합니다.

func SetupTestDatabase() (testcontainers.Container, *pgxpool.Pool) {
    // ... (see before)

    // 3.2 Create db connection string and connect
    dbURI := fmt.Sprintf("postgres://postgres:postgres@%v:%v/testdb", host, port.Port())
    MigrateDb(dbURI)
    connPool, _ := pgxpool.Connect(context.Background(), dbURI)

    return dbContainer, connPool
}

메서드MigrateDb(dbURI)golang-migrate을 사용하여 데이터베이스 마이그레이션을 데이터베이스에 적용합니다. 마이그레이션 스크립트는 애플리케이션의 바이너리에 포함된 디렉토리migrations에서 읽습니다.

//go:embed migrations
var migrations embed.FS

func MigrateDb(dbURI string) error {
    source, _ := iofs.New(migrations, "migrations")

    m, _ := migrate.NewWithSourceInstance("iofs", source, strings.Replace(dbURI, "postgres://", "pgx://", 1))
    defer m.Close()

    err = m.Up()
    if err != nil && !errors.Is(err, migrate.ErrNoChange) {
        return err
    }

    return nil
}

마무리



PostgreSQL을 사용하여 Docker에서 실행 중인 실제 Testcontainers 데이터베이스에 대한 통합 테스트를 위한 작업 설정이 있습니다. 지속성 계층의 통합 테스트에 이 설정을 사용할 수 있습니다. 그러나 그것이 좋은 전부는 아닙니다.

이 설정은 인프라가 필요한 모든 종류의 통합 테스트에 적합한 방법입니다. 예를 들어 mail_resource_smtp_test.go 에서와 같이 도커에서 실행 중인 메일 서버로 이메일을 보내는 테스트입니다.


리마스트 / 테스트용 컨테이너






테스트 컨테이너로 이동


내 게시물에 대한 샘플 코드입니다.


View on GitHub

좋은 웹페이지 즐겨찾기