Go 테스트 코드 작성 정리: testing 패키지와 Go식 테스트 문화
로깅과 설정까지 정리했다면, 이제 운영 관점에서 빠질 수 없는 마지막 기본 주제인
테스트(testing) 를 다룰 차례다.
Go는 테스트를 “특별한 작업”으로 취급하지 않는다.
별도의 프레임워크를 강요하지도 않고,
테스트 코드 역시 일반 Go 코드의 연장선으로 다룬다.
이 글에서는
- Go의 testing 패키지 기본 사용법
- 테스트 코드 파일 구조
- 실무에서 자주 쓰는 테스트 작성 기준
을 중심으로 정리한다.
Go 테스트의 기본 철학
먼저 Go 테스트의 전제를 하나 짚고 가는 게 좋다.
Go 테스트는
많이, 자주, 가볍게 작성하는 것을 전제로 한다.
- 테스트 러너 설치 필요 없음
- IDE 의존도 낮음
- CI 환경에서도 동일하게 동작
덕분에 테스트는
“나중에 여유 되면”이 아니라
개발 흐름 안에 자연스럽게 포함되기 쉽다.
testing 패키지 기본 구조
Go 테스트는 testing 패키지를 사용한다.
import "testing"
테스트 함수는 다음 규칙을 따른다.
func TestSomething(t *testing.T) {
// test logic
}
- 함수 이름은 반드시 Test로 시작
- 인자로 *testing.T를 받는다
- _test.go 파일에 작성
테스트 파일은 어디에 두나
일반적으로 다음과 같은 구조를 가진다.
user/
├── service.go
└── service_test.go
- 테스트 대상 파일과 같은 패키지
- 파일명은 _test.go로 끝남
이 구조 덕분에
테스트 대상 코드를 찾기도 쉽고,
패키지 단위 테스트 흐름도 자연스럽다.
가장 기본적인 테스트 예제
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
result := Add(1, 2)
if result != 3 {
t.Errorf("expected 3, got %d", result)
}
}
- 기대값과 실제값 비교
- 실패 시 t.Errorf, t.Fatalf 사용
처음에는 이 정도만으로도 충분하다.
테스트는 단순할수록 유지보수가 쉽다.
go test 명령어 사용법
go test ./...
- 현재 모듈 전체 테스트 실행
- 하위 패키지까지 모두 포함
go test ./user
- 특정 패키지만 테스트
실무에서는
CI 단계에서 go test ./...를 기본으로 돌리는 경우가 많다.
Table-Driven Test: Go 테스트의 핵심 패턴
Go 테스트에서 가장 자주 등장하는 패턴이다.
func TestAdd_Table(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"simple", 1, 2, 3},
{"zero", 0, 0, 0},
{"negative", -1, 1, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("expected %d, got %d", tt.want, got)
}
})
}
}
- 테스트 케이스를 데이터로 표현
- 중복 코드 감소
- 실패 케이스 파악이 쉬움
실제로 써보면,
테스트가 늘어날수록 이 방식의 장점이 확실해진다.


테스트 실패 처리 기준
테스트에서 자주 쓰는 메서드는 다음 정도다.
- t.Error / t.Errorf
→ 실패 기록 후 계속 진행 - t.Fatal / t.Fatalf
→ 즉시 테스트 중단
기본적인 기준은 다음과 같다.
- 이후 검증이 의미 없으면 Fatal
- 여러 케이스를 한 번에 보고 싶으면 Error
일관된 기준을 정해두는 게 중요하다.
테스트에서 의존성 다루기
테스트를 어렵게 만드는 가장 큰 요인은
외부 의존성(DB, 네트워크, API) 이다.
이때 Go에서는
앞서 배운 interface 기반 설계가 큰 힘을 발휘한다.
- 실제 구현 대신 mock 사용
- 테스트에서는 인터페이스만 의존
이 구조를 염두에 두고 코드를 작성하면,
테스트를 “나중에 붙이는 작업”이 아니라
자연스럽게 가능한 구조로 만들 수 있다.
테스트는 문서 역할도 한다
잘 작성된 테스트는
다음 역할을 동시에 수행한다.
- 코드가 어떻게 동작하는지 보여줌
- 경계 조건을 명확히 드러냄
- 리팩터링 시 안전망 제공
실무에서는
“이 함수 어떻게 쓰는 거지?”라는 질문보다
테스트 코드를 먼저 보는 경우도 적지 않다.
과도한 테스트는 피해야 한다
⚠️ 주의할 점
- 내부 구현에 과하게 의존하는 테스트
- 리팩터링만 해도 깨지는 테스트
- 의미 없는 커버리지 채우기
Go 테스트의 목적은
“완벽함”이 아니라
변경에 대한 신뢰를 확보하는 데 있다.
정리
Go의 테스트는
가볍게 시작해서 꾸준히 쌓아가는 문화에 가깝다.
- testing 패키지만으로 충분
- Table-Driven Test 적극 활용
- 인터페이스 기반 설계와 궁합이 좋다
여기까지 정리했다면,
Go 언어의 기초부터 실무 진입까지 필요한 큰 흐름은 한 번 모두 훑은 셈이다.
다음 단계로는
- 실제 웹 서버 예제(API 서버 만들기)
- 간단한 프로젝트 실습 시리즈
- 성능 튜닝과 프로파일링(pprof)
중 하나로 이어가면,
이론과 실무를 자연스럽게 연결할 수 있다.