最終更新:2020/05/14
目次
- テーブルドリブンテストとは
- テーブルドリブンテストの書き方
- テーブルドリブンテストの欠点
1. テーブルドリブンテストとは
Go言語のテーブルドリブンテストは、テスト手法のデータドリブンテストのことです。
データドリブンテストは、コードからテストの入力と出力値が区切られているテストの方法です。
2. テーブルドリブンテストの書き方
テーブルドリブンテストは以下のようなコードになります。
package usecase_test
import (
"errors"
"net/http"
"testing"
"github.com/golang/mock/gomock"
)
const (
CaseEmptyUserID = 0
CaseInvalidUserID = 1
CaseNotFoundUser = 2
CaseUserDeleteFailure = 3
CaseUserDeleteSuccess = 4
)
func TestUserDeleteUseCaseDo(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cases := []struct {
UserID string
IntUserID int
TestPattern int
}{
{UserID: "", IntUserID: 0, TestPattern: CaseEmptyUserID},
{UserID: "aaaaa", IntUserID: 0, TestPattern: CaseInvalidUserID},
{UserID: "100", IntUserID: 100, TestPattern: CaseNotFoundUser},
{UserID: "100", IntUserID: 100, TestPattern: CaseUserDeleteFailure},
{UserID: "100", IntUserID: 100, TestPattern: CaseUserDeleteSuccess},
}
for _, c := range cases {
mockUsersRepogitory := NewMockUsersRepogitory(ctrl)
if c.TestPattern == CaseNotFoundUser {
mockUsersRepogitory.EXPECT().FindByID(c.IntUserID).Return(nil, errors.New(config.GormRecordNotFound))
} else if c.TestPattern == CaseUserDeleteFailure {
user, _ := fixture.FindByUserID(c.IntUserID)
mockUsersRepogitory.EXPECT().FindByID(c.IntUserID).Return(user, nil)
mockUsersRepogitory.EXPECT().DeleteByUserID(c.IntUserID).Return(errors.New("somehing"))
} else if c.TestPattern == CaseUserDeleteSuccess {
user, _ := fixture.FindByUserID(c.IntUserID)
mockUsersRepogitory.EXPECT().FindByID(c.IntUserID).Return(user, nil)
mockUsersRepogitory.EXPECT().DeleteByUserID(c.IntUserID).Return(nil)
}
u := usecase.NewUserDeleteUseCase(mockUsersRepogitory)
code, obj := u.Do(c.IntUserID, nil)
switch c.TestPattern {
case CaseEmptyUserID:
assertFailure(code, http.StatusBadRequest, config.RequiredUserID, obj, t)
case CaseInvalidUserID:
assertFailure(code, http.StatusBadRequest, config.InvalidUserID, obj, t)
case CaseNotFoundUser:
assertFailure(code, http.StatusBadRequest, config.UserNotFound, obj, t)
case CaseUserDeleteFailure:
assertFailure(code, http.StatusInternalServerError, config.UnexpectedErrorCode, obj, t)
case CaseUserDeleteSuccess:
assertSuccess(code, http.StatusNoContent, t)
default:
}
}
}
func assertFailure(code int, expectCode int, expectErrorCode string, obj interface{}, t *testing.T) {
if code != expectCode {
t.Errorf("got %v, want %v", code, expectCode)
}
result := obj.(api_response.Response)
if result.Errors[0].Code != expectErrorCode {
t.Errorf("got %v, want %v", result.Errors[0].Code, expectErrorCode)
}
}
func assertSuccess(code int, expectCode int, t *testing.T) {
if code != expectCode {
t.Errorf("got %v, want %v", code, expectCode)
}
}
このコードは、ユーザー情報を削除するAPIのテーブルドリブンテストです。
テストパターンとして
- ユーザIDが空白
- ユーザIDが無効
- ユーザ情報なし
- ユーザ削除失敗
- ユーザ削除成功
を定義しています。
テーブルドリブンテストのポイントは
テストケースを宣言する
ことです。
cases := []struct {
UserID string
IntUserID int
TestPattern int
}{
{UserID: "", IntUserID: 0, TestPattern: CaseEmptyUserID},
{UserID: "aaaaa", IntUserID: 0, TestPattern: CaseInvalidUserID},
{UserID: "100", IntUserID: 100, TestPattern: CaseNotFoundUser},
{UserID: "100", IntUserID: 100, TestPattern: CaseUserDeleteFailure},
{UserID: "100", IntUserID: 100, TestPattern: CaseUserDeleteSuccess},
}
このことにより、一つのテスト関数で複数のテストを実行することができます。
なので、重複せざるを得ないテストコードを減らすことができます。
3. テーブルドリブンテストの欠点
テーブルドリブンテストの欠点は、
コードの複雑度が高くなる
ことです。
テストをコードにひとまとめにするのだから当たり前とも言えます。
なので、
gocyclo
のような複雑度を測るツールを導入しているとテストでひっかかります。
これを防ぐには
- テストコードを分割する
- テストコードは複雑度を計測しない
- データドリブンテストは利用しない
という手段があります。
一番のオススメは
テストコードを分割する
です。
面倒ですが、可読性が良くなるのでテストも変更に強くなります。
欠点は、テストコードを書くにも工数がかかることです。