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言語】テーブルドリブンテストの実装方法と活用例 〜実践的なテストコードの書き方〜

Posted at

【Go言語】テーブルドリブンテストの実践的な実装方法と活用例

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

Go言語での開発において、効率的なテスト実装は非常に重要です。今回は、実際のプロジェクト例を用いながら、テーブルドリブンテストの実装方法とその活用について解説します。

テーブルドリブンテストとは?

テーブルドリブンテストは、テストケースをデータとして管理し、反復的に処理することでコードを簡潔かつ明瞭に記述するテスト手法です。この手法を使うことで、テストケースの追加や修正が容易になり、コードの保守性も向上します。

テストの基本的な流れ

テーブルドリブンテストは、以下の3つのステップで実装します:

  1. テストケース名とテストデータのスライスを作成
  2. for文でスライスをループ処理
  3. サブテストの実行

実装例

今回は、記事の一覧・詳細を取得する簡易ブログシステムを例に説明します。

1. テストの前処理・後処理

package repositories_test

import (
    "database/sql"
    "fmt"
    "os"
    "testing"

    _ "github.com/go-sql-driver/mysql"
)

var testDB *sql.DB

func TestMain(m *testing.M) {
    // 前処理
    if err := setup(); err != nil {
        fmt.Println("Failed to set up test environment:", err)
        os.Exit(1)
    }

    // テスト実行
    code := m.Run()

    // 後処理
    teardown()

    os.Exit(code)
}

func setup() error {
    dbUser := "hogeuser"
    dbPassword := "hogepass"
    dbDatabase := "hogedb"
    dbConn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?parseTime=true", 
        dbUser, dbPassword, dbDatabase)

    var err error
    testDB, err = sql.Open("mysql", dbConn)
    return err
}

func teardown() {
    testDB.Close()
}

2. 通常のテスト実装例

func TestSelectArticleList(t *testing.T) {
    expectedNum := 3
    got, err := repositories.SelectArticleList(testDB, 1)
    if err != nil {
        t.Fatal(err)
    }

    if len(got) != expectedNum {
        t.Errorf("Expected %d articles, but got %d", expectedNum, len(got))
    }
}

3. テーブルドリブンテストの実装例

func TestSelectArticleDetail(t *testing.T) {
    tests := []struct {
        testTitle string
        expected  models.Article
    }{
        {
            testTitle: "正常系:最初の投稿を取得",
            expected: models.Article{
                ID:       1,
                Title:    "firstPost",
                Contents: "This is my first blog",
                UserName: "ikeda",
                NiceNum:  3,
            },
        },
        {
            testTitle: "正常系:2番目の投稿を取得",
            expected: models.Article{
                ID:       2,
                Title:    "2nd",
                Contents: "Second blog post",
                UserName: "ikeda",
                NiceNum:  4,
            },
        },
    }

    for _, test := range tests {
        t.Run(test.testTitle, func(t *testing.T) {
            got, err := repositories.SelectArticleDetail(testDB, test.expected.ID)
            if err != nil {
                t.Fatal(err)
            }

            if got.ID != test.expected.ID {
                t.Errorf("Expected ID %d, but got %d", 
                    test.expected.ID, got.ID)
            }
            if got.Title != test.expected.Title {
                t.Errorf("Expected Title %s, but got %s", 
                    test.expected.Title, got.Title)
            }
            // その他のフィールドの検証も同様に実装
        })
    }
}

実装のポイント

  1. テストケースの構造体定義

    • テストケースごとに期待値と入力値を明確に定義
    • テストケース名は具体的な内容を記述
  2. サブテストの活用

    • t.Runを使用して各テストケースを独立して実行
    • テスト失敗時に該当するケースが特定しやすい
  3. エラーメッセージの充実

    • 期待値と実際の値を明確に表示
    • どのフィールドで失敗したかを具体的に示す

テストの実行方法

# 詳細な出力でテストを実行
go test -v

# 特定のテストのみを実行
go test -v -run TestSelectArticleDetail

実行結果の例:

=== RUN   TestSelectArticleDetail
=== RUN   TestSelectArticleDetail/正常系:最初の投稿を取得
--- PASS: TestSelectArticleDetail/正常系:最初の投稿を取得 (0.01s)
=== RUN   TestSelectArticleDetail/正常系:2番目の投稿を取得
--- PASS: TestSelectArticleDetail/正常系:2番目の投稿を取得 (0.01s)
PASS

まとめ

テーブルドリブンテストのメリット:

  • テストケースの追加・修正が容易
  • コードの重複を防ぎ、保守性が向上
  • テスト結果が明確で理解しやすい
  • 複数のパターンを効率的にテスト可能

この手法を活用することで、より堅牢なテストを効率的に実装することができます。ぜひ、みなさんのプロジェクトでも取り入れてみてください!

参考文献

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?