Open2

Gormで実DBをテストする際のベストプラクティス

じょうげんじょうげん

ネストしたトランザクションを使えば、テストケース間の干渉を防ぐことができる。
トランザクション内で行った作業はその外の世界の影響を受けない。
そのトランザクション内であればコミット前のデータも扱うことができるため、テストケース毎にそれぞれトランザクションを開始し、そのトランザクション内で検証を行い、テストが終わったらロールバックすることでデータベースにデータを残すことなくテストを終えることができる。
Gormはネストしたトランザクションにも対応しているため、その内部で更にトランザクションを開始してもトランザクション内でトランザクションを開始した扱いとなり整合性が保たれる。
わざわざデータベースをテストケースごとに立ち上げることなく、それぞれのテストケースでの整合性を保てる!

https://gorm.io/ja_JP/docs/transactions.html

t.Run(name, func(t *testing.T) {
	t.Parallel()
    tx := conn.Begin()
    defer tx.Rollback()
    // Given
    req := httptest.NewRequest(http.MethodPost, url, )
    rec := httptest.NewRecorder()
    c := e.NewContext(req, rec)
    c.SetPath(url)
    c.Set(keys.DB, tx)
    
    tx.Exec(tc.SQL)
    
    // When
    result := server.PostFoo(c))
    
    // Then
    if assert.NoError(t, result) {
        assert.Equal(t, tc.ExpectedCode, rec.Code) 
        ignoreFields := []string{"id","createdAt", "deletedAt"}
        testutils.AssertEqualJSON(t, tc.ExpectedBody, rec.Body.String(), ignoreFields...)
    }
})
じょうげんじょうげん

Transactionを扱うための関数は2種類ある。

  • db.Transaction(func (*gorm.DB) error) error
    高階関数で、処理を実行する関数を渡す方式

  • db.Begin() *gorm.DB
    戻り値を使って手動でTransactionの開始終了をコントロールする方式

後者の場合、errorが返ってきたときに手動でtx.Rollback()を呼ぶ必要があり冗長。
更に、ドキュメントには書いていないがdb.Begin()ではネストさせるとエラーが発生する。
前者の方式を使うことを推奨。