0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go言語】サービス層のテスト実装とt.Cleanupを使った効率的なテストデータ管理

Posted at

【Go言語】サービス層の実装とテスト環境構築のベストプラクティス

こんにちは!フリーランスエンジニアのこたろうです。

Goでのバックエンド開発において、適切なサービス層の実装とテスト環境の構築は非常に重要です。今回は、t.Cleanupを活用したテストデータの管理方法と、実践的なサービス層の実装について解説します。

サービス層の実装とは?

サービス層は、ビジネスロジックを担う重要な層です。以下のような特徴があります:

  • ビジネスロジックの集約
  • データベース操作の抽象化
  • テスタビリティの確保

基本的な実装例

1. サービス構造体の定義

package services

type ArticleService struct {
    DB Database // インターフェースを使用して依存性を注入
}

// Databaseインターフェース
type Database interface {
    SelectArticleDetail(id int) (Article, error)
    Close() error
}

// 記事取得のメソッド
func (s *ArticleService) GetArticleDetail(articleID int) (Article, error) {
    article, err := s.DB.SelectArticleDetail(articleID)
    if err != nil {
        return Article{}, fmt.Errorf("failed to get article: %w", err)
    }
    return article, nil
}

テスト環境の構築

1. t.Cleanupの活用

t.Cleanupは、テスト後の後処理を簡潔に書けるGo 1.14から導入された機能です。

func TestGetArticleDetail(t *testing.T) {
    // テストDB設定
    db := setupTestDB(t)
    // テスト終了時にDBをクローズ
    t.Cleanup(func() {
        if err := db.Close(); err != nil {
            t.Errorf("failed to close db: %v", err)
        }
    })

    // サービスの初期化
    svc := &services.ArticleService{DB: db}

    // テストケース実行
    tests := []struct {
        name     string
        articleID int
        want     Article
        wantErr  bool
    }{
        {
            name:     "正常系: 記事が見つかる",
            articleID: 1,
            want:     Article{ID: 1, Title: "テスト記事"},
            wantErr:  false,
        },
        {
            name:     "異常系: 記事が見つからない",
            articleID: 999,
            want:     Article{},
            wantErr:  true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := svc.GetArticleDetail(tt.articleID)
            if (err != nil) != tt.wantErr {
                t.Errorf("GetArticleDetail() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("GetArticleDetail() = %v, want %v", got, tt.want)
            }
        })
    }
}

2. テストデータの管理

func setupTestDB(t *testing.T) *sql.DB {
    db, err := sql.Open("postgres", "postgres://user:pass@localhost:5432/testdb?sslmode=disable")
    if err != nil {
        t.Fatalf("failed to connect to test database: %v", err)
    }

    // テストデータの準備
    if err := prepareTestData(db); err != nil {
        t.Fatalf("failed to prepare test data: %v", err)
    }

    // テスト終了時にテストデータを削除
    t.Cleanup(func() {
        if err := cleanupTestData(db); err != nil {
            t.Errorf("failed to cleanup test data: %v", err)
        }
    })

    return db
}

func prepareTestData(db *sql.DB) error {
    queries := []string{
        `INSERT INTO articles (id, title, content) 
         VALUES (1, 'テスト記事', 'これはテスト用の記事です')`,
        `INSERT INTO articles (id, title, content) 
         VALUES (2, '別のテスト記事', 'これは別のテスト用記事です')`,
    }

    for _, q := range queries {
        if _, err := db.Exec(q); err != nil {
            return fmt.Errorf("failed to execute query: %w", err)
        }
    }
    return nil
}

func cleanupTestData(db *sql.DB) error {
    _, err := db.Exec("DELETE FROM articles WHERE id IN (1, 2)")
    return err
}

テスト実装のポイント

  1. t.Cleanupの活用

    • テスト後の後処理を簡潔に記述
    • 複数の後処理を順番に登録可能
    • deferよりも柔軟な使用が可能
  2. テストデータの管理

    • テストデータの準備と削除を明確に分離
    • トランザクションを活用したロールバック
    • テストケースごとに独立した環境を維持
  3. テーブルドリブンテストの活用

    • 複数のテストケースを効率的に管理
    • エッジケースの網羅的なテスト
    • テストケースの追加が容易

まとめ

サービス層のテスト実装において重要なポイント:

  • t.Cleanupを活用した効率的な後処理
  • テストデータの適切な準備と削除
  • インターフェースを活用した依存性の注入
  • テーブルドリブンテストによる網羅的なテスト

これらの方法を組み合わせることで、保守性が高く、信頼性のあるテストを実装することができます。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?