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で直接関数呼び出しを含むコードをテスト可能にする最もシンプルな方法を紹介します。

テスト難しい直接関数呼び出し

次のようなコードがあるとします:

func ProcessUser(userID string) (*ProcessedData, error) {
    // 直接関数を呼び出している - テスト時に置き換えられない
    user, err := GetUserFromDB(userID)
    if err != nil {
        return nil, err
    }
    
    // 結果の作成
    return &ProcessedData{
        UserName: user.Name,
        ProcessedAt: time.Now(),
    }, nil
}

// 実際のデータベースアクセスを行う関数
func GetUserFromDB(id string) (*User, error) {
    // 実際のDB処理(テスト時には実行したくない)
    db, err := sql.Open("mysql", "user:password@/dbname")
    if err != nil {
        return nil, err
    }
    defer db.Close()
    
    // データベース処理...
    return &User{ID: id, Name: "John"}, nil
}

シンプルな解決策:関数変数化

わずか2行の変更で、テスト可能なコードに変えられます:

// 通常の関数定義をそのまま残す
func GetUserFromDB(id string) (*User, error) {
    // 実際のDB処理
    db, err := sql.Open("mysql", "user:password@/dbname")
    if err != nil {
        return nil, err
    }
    defer db.Close()
    
    // データベース処理...
    return &User{ID: id, Name: "John"}, nil
}

// 変数に関数を代入
var GetUserFromDBFunc = GetUserFromDB

// ProcessUser関数も少し修正
func ProcessUser(userID string) (*ProcessedData, error) {
    // 直接呼び出す代わりに変数経由で呼び出す
    user, err := GetUserFromDBFunc(userID)  // ここだけ変更
    if err != nil {
        return nil, err
    }
    
    return &ProcessedData{
        UserName: user.Name,
        ProcessedAt: time.Now(),
    }, nil
}

モックを使ったテスト

この変更により、テスト時に関数をモックに置き換えることが可能になります:

func TestProcessUser(t *testing.T) {
    // 元の関数を保存
    originalFunc := GetUserFromDBFunc
    
    // テスト終了時に元に戻す
    defer func() {
        GetUserFromDBFunc = originalFunc
    }()
    
    // モック関数を設定
    GetUserFromDBFunc = func(id string) (*User, error) {
        // テスト用のモック実装
        return &User{ID: id, Name: "Test User"}, nil
    }
    
    // テスト実行
    result, err := ProcessUser("123")
    
    // 検証
    assert.NoError(t, err)
    assert.Equal(t, "Test User", result.UserName)
}

エラーケースのテスト

エラーパターンも同様に簡単にテストできます:

func TestProcessUser_Error(t *testing.T) {
    // 元の関数を保存して後で復元
    originalFunc := GetUserFromDBFunc
    defer func() { GetUserFromDBFunc = originalFunc }()
    
    // エラーを返すモック関数
    GetUserFromDBFunc = func(id string) (*User, error) {
        return nil, errors.New("database error")
    }
    
    // テスト実行
    result, err := ProcessUser("123")
    
    // 検証
    assert.Error(t, err)
    assert.Nil(t, result)
}

この方法のメリット

  • コード変更が最小限(たった2行)
  • シンプルで理解しやすい
  • 既存コードへの影響が少ない
  • 特別なライブラリやフレームワークが不要

まとめ

Goで関数を変数化する方法は、テスタビリティを向上させる最もシンプルな手法の一つです。既存のコードに対しても最小限の変更で導入できるため、レガシーコードのリファクタリングや、急いでテストを書く必要がある場合に非常に有効です。

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?