Quick Flow
go test ./...
go test -run '^TestParsePort$' ./internal/config
go test -count=1 ./...func TestAdd(t *testing.T) {
got := Add(1, 2)
if got != 3 {
t.Fatalf("got %d, want %d", got, 3)
}
}Go 테스트는 별도 프레임워크보다 표준 testing 패키지와 명확한 케이스 목록을 먼저 봅니다.
기본 흐름
테스트 파일은 _test.go로 끝난다
calc.go
calc_test.go테스트 함수는 TestXxx(t *testing.T) 형태입니다.
func TestParsePort(t *testing.T) {
port, err := ParsePort("8080")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if port != 8080 {
t.Fatalf("got %d, want %d", port, 8080)
}
}t.Fatal과 t.Error는 흐름이 다르다
t.Fatal,t.Fatalf: 실패 후 현재 테스트 중단t.Error,t.Errorf: 실패 기록 후 계속 진행
필수 전제가 깨지면 Fatal, 여러 케이스를 계속 확인하려면 Error가 더 맞습니다.
t.Fatal, t.Fatalf, t.Skip는 테스트 함수를 실행하는 goroutine에서만 호출합니다. worker goroutine 안에서 오류가 나면 channel이나 errgroup으로 test goroutine에 전달한 뒤 그곳에서 t.Fatal을 호출합니다.
테이블 테스트
여러 입력/출력을 표로 둔다
Go에서는 같은 함수의 다양한 입력을 struct slice로 두는 table-driven test가 자주 쓰입니다.
func TestIsAdult(t *testing.T) {
tests := []struct {
name string
age int
want bool
}{
{name: "minor", age: 19, want: false},
{name: "adult", age: 20, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := IsAdult(tt.age)
if got != tt.want {
t.Fatalf("got %v, want %v", got, tt.want)
}
})
}
}t.Run을 쓰면 실패한 케이스 이름이 보이고, 특정 케이스만 골라 실행하기도 쉬워집니다.
subtest를 병렬로 돌릴 때는 shared state와 환경 변수, 현재 디렉터리처럼 process 전체에 영향을 주는 상태를 섞지 않습니다. Go 1.21 이하 언어 버전도 지원한다면 loop variable을 명시적으로 다시 묶어 closure가 다른 케이스를 가리키지 않게 합니다.
for _, tt := range tests {
tt := tt // Go 1.21 이하도 안전하게 지원
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := IsAdult(tt.age); got != tt.want {
t.Fatalf("got %v, want %v", got, tt.want)
}
})
}go test는 성공한 package 결과를 cache에서 재사용할 수 있습니다. 외부 상태를 재현하거나 방금 수정한 실행을 강제로 다시 확인할 때는 -count=1, 특정 이름만 볼 때는 -run을 사용합니다.
helper는 t.Helper()로 표시한다
func mustParse(t *testing.T, raw string) int {
t.Helper()
value, err := strconv.Atoi(raw)
if err != nil {
t.Fatalf("parse %q: %v", raw, err)
}
return value
}t.Helper()를 붙이면 실패 위치가 helper 내부가 아니라 호출한 테스트 쪽으로 더 잘 잡힙니다.
선택 기준
| 상황 | 먼저 떠올릴 선택 |
|---|---|
| 패키지 전체 테스트 | go test ./... |
| 필수 전제 실패 | t.Fatal / t.Fatalf |
| 여러 검증을 계속 진행 | t.Error / t.Errorf |
| 입력/출력 조합이 여러 개 | table-driven test |
| 케이스별 이름과 부분 실행 | t.Run |
| 테스트 helper 작성 | t.Helper() |
주의할 점
table-driven test는 케이스가 많을 때 강력하지만, 표가 너무 커지면 무엇을 검증하는지 흐려질 수 있습니다. 같은 실패 원인을 공유하는 케이스끼리 묶고, 경계값과 대표 정상값을 먼저 보이게 두는 편이 읽기 좋습니다.
참고 링크
3 sources