メインコンテンツまでスキップ

テスト戦略

Bazbiiシステムのテスト方針と実装パターンを説明します。

テスト戦略の概要

テストピラミッド

        /\
/E2E\ ← 少数(統合テスト)
/------\
/ Integration \ ← 中程度
/--------------\
/ Unit Tests \ ← 多数(単体テスト)
/------------------\

テストの方針

  1. 単体テスト: server-coreのビジネスロジックは高カバレッジを目指す
  2. 統合テスト: コンポーネント間の結合をテスト
  3. E2Eテスト: 主要なユーザーフローのテスト(必要に応じて)

各レイヤーでのテスト

server-core(ドメインロジック)

目標: 高カバレッジ(80%以上)

  • テスト対象: ビジネスロジック、ドメインモデル、バリデーション
  • モック使用: 最小限(インターフェースのモックは使用可)
  • テストデータ: ドメインモデルを直接構築
// 例: PostServiceのテスト
func TestPostService_CreateUserPost(t *testing.T) {
// Given: モックリポジトリを準備
repo := &MockPostRepository{}
service := post.NewService(repo)

// When: 投稿を作成
id, err := service.CreateUserPost(ctx, post.CreateUserPostInput{
H3Index: h3Index,
Text: "test",
ActorID: actorID,
})

// Then: 結果を検証
assert.NoError(t, err)
assert.NotNil(t, id)
assert.True(t, repo.InsertCalled)
}

server-platform(実装)

目標: 重要な実装はテスト

  • テスト対象: リポジトリ実装、外部サービス連携
  • テストデータベース: 実際のPostgreSQLを使用(統合テスト)
  • テストコンテナ: Docker ComposeでDBを起動
// 例: PostRepositoryの統合テスト
func TestPostRepo_Insert(t *testing.T) {
// テスト用DBセットアップ
db := setupTestDB(t)
repo := postgres.NewPostRepo(db)

// テスト実行
id, err := repo.Insert(ctx, testPost)
assert.NoError(t, err)

// データ確認
got, err := repo.FindByID(ctx, id)
assert.NoError(t, err)
assert.Equal(t, testPost, got)
}

server-apps(アプリケーション層)

目標: ハンドラーの基本動作をテスト

  • テスト対象: リクエスト/レスポンスのマッピング、バリデーション
  • モック使用: gRPCクライアント、サービス層をモック
// 例: Gatewayハンドラーのテスト
func TestHeatmapHandler_GetHeatmap(t *testing.T) {
// Given: モックAPIクライアント
mockClient := &MockHeatmapClient{}
handler := NewHeatmapHandler(mockClient)

// When: リクエスト処理
req := connect.NewRequest(&feedv1.GetHeatmapRequest{
H3Res: 9,
})
resp, err := handler.GetHeatmap(ctx, req)

// Then: レスポンス検証
assert.NoError(t, err)
assert.NotNil(t, resp)
}

テストパターン

1. リポジトリパターンのテスト

原則: インターフェースと実装を分離してテスト

  • インターフェース: モックでビジネスロジックをテスト
  • 実装: 統合テストでデータベース操作をテスト

2. ドメインイベントのテスト

func TestPostService_CreateUserPost_EmitsEvent(t *testing.T) {
// Given: イベントパブリッシャーをモック
publisher := &MockEventPublisher{}
service := post.NewService(repo, publisher)

// When: 投稿を作成
service.CreateUserPost(ctx, input)

// Then: イベントが発行されたことを確認
assert.True(t, publisher.PublishCalled)
assert.Equal(t, "post.created", publisher.LastEvent.EventType())
}

3. エラーハンドリングのテスト

func TestPostService_CreateUserPost_InvalidH3Index(t *testing.T) {
// Given: 無効なH3インデックス
invalidH3 := H3Index(0)

// When: 投稿を作成
_, err := service.CreateUserPost(ctx, post.CreateUserPostInput{
H3Index: invalidH3,
Text: "test",
ActorID: actorID,
})

// Then: エラーが返る
assert.Error(t, err)
assert.Equal(t, post.ErrInvalidH3Index, err)
}

テスト実行

コマンド

# 全モジュールのテスト実行
make go/test

# 特定のパッケージのテスト
cd server-core/post
go test ./...

# カバレッジレポート
go test ./... -cover

# 詳細なカバレッジレポート
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out

テストデータベースのセットアップ

# テスト用DBコンテナ起動
make compose/db.up

# マイグレーション実行
make db/mig-up

テストカバレッジ

目標カバレッジ

  • server-core: 80%以上
  • server-platform: 60%以上(重要な実装は高カバレッジ)
  • server-apps: 50%以上(主要なハンドラーはテスト)

カバレッジレポート

# カバレッジレポート生成
make go/test-coverage

# レポート確認
open coverage.html

フロントエンドテスト

ユニットテスト

  • ツール: Jest, React Testing Library
  • 対象: コンポーネント、カスタムフック

E2Eテスト

  • ツール: Playwright(検討中)
  • 対象: 主要なユーザーフロー

ベストプラクティス

1. テストの独立性

  • 各テストは独立して実行可能
  • テスト間で状態を共有しない

2. テストデータの管理

  • テストデータはテスト内で構築
  • グローバルなテストデータベースは避ける

3. モックの適切な使用

  • ビジネスロジックはモックでテスト
  • 実装のテストは統合テストで行う

4. テストの可読性

  • Given-When-Thenパターンを使用
  • テスト名は何をテストするか明確に

関連ドキュメント