Go 테스트 2026: 유닛 테스트, 모킹, 기술 면접 질문
테이블 기반 테스트, 모킹, 면접관이 기대하는 패턴으로 Go 테스트를 마스터합니다. testify와 gomock을 활용한 실용적인 예제를 다룹니다.

Go 테스트는 모든 Go 설치에 포함되어 있고 외부 의존성이 필요 없는 표준 testing 패키지에 의존합니다. 다른 언어의 프레임워크와 달리 Go의 접근 방식은 단순함을 지향합니다. 테스트 파일은 프로덕션 코드와 함께 위치하고, 테스트 함수는 명명 규칙을 따르며, go test 명령이 검색과 실행을 처리합니다.
테이블 기반 테스트를 작성하고, 의존성 주입을 위해 인터페이스를 사용하며, 모킹이 가치를 더하는 경우와 노이즈를 더하는 경우를 이해하는 지원자가 높이 평가됩니다.
testing 패키지로 유닛 테스트 작성하기
모든 테스트 파일은 _test.go로 끝나며 테스트 대상 코드와 동일한 패키지에 위치합니다. 테스트 함수는 Test로 시작하고 그 뒤에 대문자로 시작하는 이름이 옵니다. *testing.T 매개변수는 실패를 보고하는 메서드를 제공합니다.
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}go test ./...를 실행하면 현재 모듈의 모든 테스트가 실행됩니다. -v 플래그는 개별 테스트 이름과 통과/실패 상태를 표시합니다.
테이블 기반 테스트: Go 표준 패턴
테이블 기반 테스트는 중복을 줄이고 새로운 케이스 추가를 쉽게 만듭니다. 테이블의 각 행은 입력과 예상 출력을 가진 하나의 시나리오를 나타냅니다. 이 패턴은 Go 표준 라이브러리와 업계 전반의 프로덕션 코드베이스에서 찾아볼 수 있습니다.
package validator
import "testing"
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "user@example.com", false},
{"missing at sign", "userexample.com", true},
{"empty string", "", true},
{"unicode local part", "user@例え.jp", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}t.Run 메서드는 독립적으로 실행되는 서브테스트를 생성합니다. 실패한 서브테스트는 다른 케이스를 중단하지 않고 어떤 특정 케이스가 실패했는지 보고합니다.
인터페이스를 통한 의존성 주입
Go 인터페이스는 외부 시스템(데이터베이스, HTTP 클라이언트, 파일 시스템)에 의존하는 코드의 테스트를 가능하게 합니다. 사용 지점에서 최소한의 인터페이스를 정의하면 실제 구현을 테스트 더블로 교체할 수 있습니다.
package order
// Repository defines the data access methods this service needs
type Repository interface {
FindByID(id string) (*Order, error)
Save(order *Order) error
}
// Service handles order business logic
type Service struct {
repo Repository
}
// NewService creates a service with the given repository
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
// Process validates and saves an order
func (s *Service) Process(order *Order) error {
if order.Total <= 0 {
return ErrInvalidTotal
}
return s.repo.Save(order)
}Service는 Repository를 만족하는 모든 타입을 받아들입니다. 프로덕션 코드는 데이터베이스 기반 구현을 전달하고, 테스트는 모킹 또는 스텁을 전달합니다.
gomock과 mockgen을 이용한 모킹
gomock 패키지는 인터페이스 정의에서 모킹 구현을 생성합니다. mockgen 명령은 소스 파일을 읽고 호출을 기록하고 설정된 값을 반환하는 모킹 구조체를 생성합니다.
# Generate mocks for the Repository interface
mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks테스트에서 생성된 모킹 사용 예시:
package order
import (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"yourmodule/order/mocks"
)
func TestService_Process_SavesValidOrder(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
order := &Order{ID: "123", Total: 99.99}
// Expect Save to be called once with this order, return nil
mockRepo.EXPECT().
Save(order).
Return(nil).
Times(1)
svc := NewService(mockRepo)
err := svc.Process(order)
assert.NoError(t, err)
}
func TestService_Process_RejectsZeroTotal(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
// No calls expected to repo since validation fails first
svc := NewService(mockRepo)
err := svc.Process(&Order{ID: "456", Total: 0})
assert.ErrorIs(t, err, ErrInvalidTotal)
}모킹은 Save가 예상된 인수로 정확히 한 번 호출되었는지 검증합니다. 테스트 대상 코드가 Save를 호출하지 않거나 다른 인수로 호출하면 테스트가 실패합니다.
testify를 이용한 단언과 스위트
testify는 수동 if문 검사보다 명확한 실패 메시지를 생성하는 단언 함수를 제공합니다. assert 패키지는 실패 후에도 실행을 계속합니다. require 패키지는 테스트를 즉시 중단합니다.
package user
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseUser(t *testing.T) {
input := `{"id": "u1", "name": "Alice"}`
user, err := ParseUser(input)
require.NoError(t, err, "parsing should not fail")
assert.Equal(t, "u1", user.ID)
assert.Equal(t, "Alice", user.Name)
assert.Empty(t, user.Email, "email should default to empty")
}require.NoError 호출은 파싱이 실패하면 테스트를 중단하여 후속 단언에서의 nil 포인터 패닉을 방지합니다.
Go 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
httptest를 이용한 HTTP 핸들러 테스트
net/http/httptest 패키지는 핸들러 출력을 캡처하기 위한 ResponseRecorder와 로컬 테스트 서버를 실행하기 위한 Server를 제공합니다. 대부분의 유닛 테스트는 네트워크 오버헤드를 피하기 위해 ResponseRecorder를 사용합니다.
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/health", nil)
rec := httptest.NewRecorder()
HealthHandler(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String())
}외부 서비스를 호출하는 핸들러의 경우 모킹 클라이언트를 주입하거나 httptest.NewServer를 사용하여 페이크 백엔드를 생성합니다.
Go 테스트에 관한 일반적인 면접 질문
면접관은 실제 코드베이스 경험을 드러내는 질문을 통해 테스트 지식을 평가합니다. 준비된 지원자를 구별하는 패턴은 다음과 같습니다:
"외부 API를 호출하는 함수를 어떻게 테스트합니까?"
HTTP 클라이언트를 위한 인터페이스를 정의하고, 함수나 구조체에 주입하며, 미리 정해진 응답을 반환하는 모킹을 제공합니다. 이렇게 하면 네트워크 호출을 피하고 테스트를 결정론적으로 만들 수 있습니다.
"스텁과 모킹을 언제 사용합니까?"
스텁은 호출 방식을 검증하지 않고 미리 준비된 응답을 반환합니다. 모킹은 특정 메서드가 특정 인수로 호출되었는지 검증합니다. 반환 값이 상호작용보다 중요할 때는 스텁을 사용합니다. 상호작용 검증이 테스트의 목적일 때는 모킹을 사용합니다.
"테이블 기반 테스트란 무엇이며 왜 Go에서 선호됩니까?"
테이블 기반 테스트는 테스트 케이스를 구조체 슬라이스의 데이터로 정의합니다. 보일러플레이트를 줄이고 케이스 추가를 쉽게 만들며 t.Run으로 읽기 쉬운 테스트 출력을 생성합니다. 이 패턴은 교묘한 추상화보다 명시적이고 반복적인 코드를 선호하는 Go의 철학에 부합합니다.
"여러 테스트가 공유하는 테스트 픽스처나 설정을 어떻게 처리합니까?"
패키지 레벨 설정과 해제에는 TestMain을 사용합니다. 관련 테스트 간 공유 설정에는 헬퍼 함수나 테스트 스위트(testify에서)를 사용합니다. 테스트 간 결합을 만드는 전역 상태를 피합니다.
Go 면접을 준비하고 계십니까? Go 테스트 모듈에서 이러한 패턴을 연습 문제와 함께 자세히 다룹니다.
커버리지와 레이스 감지를 이용한 테스트 실행
go test 명령은 테스트되지 않은 코드 경로와 동시성 버그를 드러내는 플래그를 받습니다.
# Run tests with coverage report
go test -cover ./...
# Generate HTML coverage visualization
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# Detect race conditions (slower, but catches real bugs)
go test -race ./...레이스 감지기는 메모리 접근을 계측하고 동일한 변수에 대한 동시 읽기와 쓰기를 플래그합니다. CI에서 -race 플래그로 테스트를 실행하면 특정 타이밍에서만 나타나는 버그를 잡을 수 있습니다.
대규모 코드베이스에서의 테스트 구성
프로젝트가 커지면 테스트 구성이 유지보수성에 영향을 미칩니다:
- 동일 패키지 테스트(
package foo)는 비공개 함수와 필드에 접근할 수 있습니다. 내부 동작을 검증하는 유닛 테스트에 사용합니다. - 외부 패키지 테스트(
package foo_test)는 외부 코드처럼 패키지를 임포트합니다. 통합 테스트와 공개 API 검증에 사용합니다. - testdata 디렉터리는 JSON 파일, 골든 출력, 시드 데이터베이스와 같은 픽스처를 저장합니다. Go는 빌드 시 이들을 무시합니다.
- 빌드 태그는 외부 시스템이 필요한 통합 테스트에서 유닛 테스트를 분리합니다.
go test -tags=integration ./...를 실행하여 포함합니다.
테스트 외의 Go 모범 사례에 대해서는 동시성 패턴 기사에서 고루틴과 채널을 다룹니다.
Go 테스트에서 기억해야 할 것들
testing패키지는 Go와 함께 제공되며 외부 의존성이 필요 없습니다- 테이블 기반 테스트는 중복을 줄이고 면접에서 기대되는 패턴입니다
- 인터페이스는 의존성 주입을 가능하게 합니다. 구현 지점이 아닌 사용 지점에서 정의합니다
gomock은 인터페이스에서 모킹을 생성합니다.testify는 더 명확한 단언을 제공합니다httptest.ResponseRecorder는 네트워크 오버헤드 없이 핸들러 출력을 캡처합니다-race플래그는 유닛 테스트에서 놓치는 동시성 버그를 감지합니다- 커버리지 지표는 주의를 안내하지만 정확성을 보장하지는 않습니다
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
Go 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 8월 24일 업데이트
태그
공유
관련 기사

Go Context 패키지 2026 완벽 가이드: 취소, 타임아웃, 면접 질문
Go 언어의 context 패키지를 상세히 해설합니다. 취소 처리, 타임아웃 설정, 요청 범위 값 활용법부터 기술 면접에서 자주 나오는 질문과 답변까지 실용적인 코드 예제와 함께 설명합니다.

Go 면접 핵심 25문항: 개발자를 위한 완전 가이드
Go 면접에 가장 많이 등장하는 25개 질문으로 합격을 노리세요. 고루틴, 채널, 인터페이스, 동시성 패턴을 코드 예제로 정리했습니다.

Go 기술 면접: Goroutine, Channel, 동시성 패턴 완벽 가이드
Go 기술 면접에서 자주 출제되는 goroutine, channel, 동시성 관련 질문을 다룹니다. 프로덕션 수준의 코드 예제와 각 답변의 설계 근거를 2026년 면접 대비용으로 상세히 설명합니다.