Gin初心者のテスト入門
Gin初心者のためのテスト入門
GoでWebアプリを構築する際に人気のフレームワーク、Ginは高速でシンプルな設計が特徴です。Gin初心者がまず取り組むべきは、テストの基本を理解することです。テストコードを書くことで、品質保証の観点からバグを早期に発見し、リリース後の修正コストを削減できます。
まずは、Ginのハンドラを単体で検証するユニットテストを作成します。テスト対象の関数を分離し、入力と期待される出力を明確に定義することで、テストケースを簡潔に保つことができます。
func TestHelloHandler(t *testing.T) {
// Arrange
req := httptest.NewRequest(http.MethodGet, "/hello", nil)
w := httptest.NewRecorder()
// Act
gin.Default().GET("/hello", HelloHandler).ServeHTTP(w, req)
// Assert
if w.Code != http.StatusOK {
t.Errorf("expected status 200, got %d", w.Code)
}
}
上記の例では、httptestパッケージを使ってリクエストとレスポンスをモックしています。これにより、実際のネットワークを介さずにハンドラの挙動を検証できます。
httptestで作るテストケース
Ginのルーティングやミドルウェアをテストする際には、httptestを活用して実際にHTTPリクエストを送信し、レスポンスを検証します。テストケースを作成する際のポイントは、入力データと期待結果を明確に定義し、再現性のあるテストを構築することです。
- リクエストパラメータやクエリ文字列を変化させる。
- 認証情報を付与したリクエストを送信し、認可ロジックを検証。
- エラーハンドリングを確認し、適切なステータスコードとメッセージが返るか確認。
以下は、複数のテストケースをまとめた例です。
func TestAuthMiddleware(t *testing.T) {
tests := []struct {
name string
token string
wantStatus int
}{
{"valid token", "valid-token", http.StatusOK},
{"missing token", "", http.StatusUnauthorized},
{"invalid token", "bad-token", http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/secure", nil)
if tt.token != "" {
req.Header.Set("Authorization", "Bearer "+tt.token)
}
w := httptest.NewRecorder()
gin.Default().GET("/secure", AuthMiddleware(), SecureHandler).ServeHTTP(w, req)
if w.Code != tt.wantStatus {
t.Errorf("expected %d, got %d", tt.wantStatus, w.Code)
}
})
}
}
このようにテストケースをテーブル駆動で書くことで、コードの重複を減らし、テストの可読性を向上させます。
モックを使ったユニットテストと統合テスト
外部サービスやデータベースに依存するロジックをテストする場合、モックを導入することでテストの高速化と安定化が図れます。Goでは、インターフェースを定義し、テスト時にモック実装を注入する手法が一般的です。
以下は、リポジトリインターフェースをモック化してユニットテストを行う例です。
type UserRepository interface {
FindByID(ctx context.Context, id int) (*User, error)
}
type MockUserRepo struct {
Users map[int]*User
}
func (m *MockUserRepo) FindByID(ctx context.Context, id int) (*User, error) {
user, ok := m.Users[id]
if !ok {
return nil, errors.New("not found")
}
return user, nil
}
func TestGetUser(t *testing.T) {
mockRepo := &MockUserRepo{Users: map[int]*User{1: {ID:1, Name:"Alice"}}}
svc := NewUserService(mockRepo)
user, err := svc.GetUser(context.Background(), 1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Alice" {
t.Errorf("expected Alice, got %s", user.Name)
}
}
モックを使うことで、実際のデータベース接続を待つ必要がなくなり、テスト実行時間を短縮できます。また、統合テストでは、モックを使わずに実際のサービスを起動し、全体のフローを検証します。統合テストは、品質保証の観点から重要であり、CIパイプラインに組み込むことでリリース前に大きな問題を発見できます。
まとめとして、Gin初心者はまず単純なユニットテストから始め、httptestで実際のHTTPリクエストを模倣し、モックを活用して外部依存を排除します。テストコードを継続的に書き、品質保証を徹底することで、堅牢で保守性の高いWebアプリケーションを構築できます。
コメント
コメントを投稿