テスト戦略
Bazbiiシステムのテスト方針と実装パターンを説明します。
テスト戦略の概要
テストピラミッド
/\
/E2E\ ← 少数(統合テスト)
/------\
/ Integration \ ← 中程度
/--------------\
/ Unit Tests \ ← 多数(単体テスト)
/------------------\
テストの方針
- 単体テスト: server-coreのビジネスロジックは高カバレッジを目指す
- 統合テスト: コンポーネント間の結合をテスト
- 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パターンを使用
- テスト名は何をテストするか明確に