sayu.day

Go 테스트 전략: 유닛·통합 분리와 Ginkgo, Testcontainers

Mock 중심 테스트만으로는 운영 장애를 막기 어렵습니다. 좋은 테스트 전략은 도구 선택보다 계층 분리가 먼저이며, 유닛 테스트와 통합 테스트가 서로 다른 책임을 지도록 나눈 뒤, CI에서 라벨 기준으로 실행 범위를 통제해야 합니다. 이 글은 유닛·통합 테스트의 책임 분리와 라벨 기반 실행 전략을 골격으로 두고, Ginkgo와 Testcontainers로 실제 인프라를 붙인 통합 테스트를 구성하는 실전 파트까지 이어서 정리합니다.

유닛과 통합 테스트의 책임 분리

유닛 테스트는 빠르고 결정적이어야 하고, 통합 테스트는 실제 인프라를 붙여 계약을 검증해야 합니다. 이 둘의 책임을 섞으면 테스트는 느려지고 신뢰도는 오히려 떨어집니다.

유닛 테스트 원칙

  • DB나 네트워크 없이 메모리 안에서 끝냅니다.
  • 입력과 출력 계약을 먼저 검증합니다.
  • 실패 메시지는 의도 중심으로 명확하게 작성합니다.

통합 테스트 원칙

  • SQL, 인덱스, 트랜잭션 동작은 실제 DB에서 검증합니다.
  • 테스트 데이터는 케이스 단위로 독립적으로 유지합니다.
  • CI에서 실행 시간을 통제하기 위해 라벨로 분리합니다. 예를 들어 PR 단계에서는 유닛 테스트와 핵심 통합 테스트만 돌리고, main 브랜치나 nightly 빌드에서는 전체 통합 테스트를 돌리는 식입니다.

구성 도구로는 Mock에 gomock 또는 hand-written fake를, 통합 테스트에 testcontainers-go를, BDD 스타일이 필요하면 ginkgo + gomega를 씁니다.

라벨 기반 CI 실행 전략

테스트가 늘어날수록 매번 전체를 실행하는 것은 비효율적입니다. Ginkgo의 라벨 필터를 활용하면 실행 범위를 명확히 나눌 수 있습니다.

ginkgo -r --label-filter='!integration'
ginkgo -r --label-filter='integration'

PR 단계에서는 10분 안에 끝나는 유닛 테스트와 핵심 통합 테스트 세트를 유지하고, 전체 통합 테스트는 main이나 nightly 빌드로 넘기는 것이 현실적인 균형입니다. 자주 하는 실수는 유닛 테스트에서 mock을 과도하게 써서 구현과 결합도가 높아지는 것, 통합 테스트가 전역 상태를 공유해 실행 순서에 의존하게 되는 것, flaky 테스트를 무시하고 재시도 횟수만 늘리는 것입니다. 실무에서는 신규 기능마다 최소 유닛 테스트 1개와 통합 테스트 1개를 갖추고, flaky가 발생하면 즉시 격리해 원인을 제거해야 합니다.

Ginkgo와 Testcontainers로 통합 테스트 구성하기

라벨로 분리한 통합 테스트를 실제로 어떻게 구성할지는 Ginkgo와 Testcontainers 조합으로 답할 수 있습니다. Ginkgo는 시나리오를 행동(behavior) 기준으로 읽기 쉽게 만들어주고, Testcontainers는 DB나 캐시를 격리된 실환경 의존성으로 띄워줍니다.

기본 구조

  1. BeforeSuite: 컨테이너를 기동하고 연결을 확인합니다.
  2. BeforeEach: 데이터를 초기화합니다.
  3. It: 비즈니스 시나리오를 실행합니다.
  4. AfterSuite: 컨테이너를 정리합니다.

핵심 원칙

  1. 테스트는 "행동" 기준으로 작성합니다.
  2. 컨테이너 초기화 실패는 즉시 fail-fast 처리합니다.
  3. 재시작, 타임아웃, 네트워크 지연 시나리오도 포함합니다.
  4. CI에서 병렬 실행 시 리소스 한도를 명시합니다.

실무 팁

컨테이너 재사용 옵션은 실행 속도에는 유리하지만, 이전 테스트의 상태가 남아 오염되지 않도록 방지 전략이 함께 있어야 합니다. 로컬과 CI의 Docker 버전 차이를 줄이기 위해 이미지 버전을 고정하는 것을 권장합니다. flaky 테스트는 발견 즉시 원인을 추적하고 격리해야 하며, 방치하면 통합 테스트 전체의 신뢰도를 갉아먹습니다.

통합 테스트의 목적은 커버리지 숫자를 높이는 것이 아니라 운영에서 터질 문제를 배포 전에 잡는 것입니다. 최소한 골든 경로(정상 흐름) 하나와 실패 경로 하나는 항상 자동화해 두는 것이 좋습니다.

정리

유닛 테스트로 빠른 피드백을, 통합 테스트로 높은 신뢰도를 확보하고, 라벨 기반 실행으로 PR과 메인 브랜치의 테스트 범위를 다르게 가져가는 것이 테스트 전략의 골격입니다. 그 위에 Ginkgo와 Testcontainers로 실제 인프라를 붙인 통합 테스트를 얹으면, 커버리지 숫자보다 실제 장애를 배포 전에 잡아내는 테스트 체계를 완성할 수 있습니다. 좋은 테스트 전략은 많이 쓰는 것이 아니라 유닛과 통합의 책임을 섞지 않고 분리해서 확보하는 것입니다.