$30 off During Our Annual Pro Sale. View Details »

Tests in Go

Tests in Go

Yunosuke Yamada

October 16, 2022
Tweet

More Decks by Yunosuke Yamada

Other Decks in Programming

Transcript

  1. テストについて Golang編
    2022/03/24

    山田悠之介

    View Slide

  2. テスト
    テストは大事。
    自動テストで担保できる部分に関しては

    自動テストをしなくてはいけない。
    業務ではフロントエンドのテストについて勉強していたが、

    バックエンドのテストが気になったので調べてみた。
    2

    View Slide

  3. 目次
    1. 普通のテスト
    2. API のテスト
    3. DB のテスト
    3

    View Slide

  4. interface と struct
    Go では interface を struct で実装することで

    オブジェクト指向のコードが書ける。
    4

    View Slide

  5. type Repository interface {

    FindAllTodos() ([]Todo, error)

    }

    type repository struct {
    db *sql.DB

    }

    func (r repository) FindAllTodos() ([]Todo, error) {
    ...

    }

    //
    返り値が Repository
    にできている

    func NewRepository(db *sql.DB) Repository {

    return repository{db}

    }

    5

    View Slide

  6. interface と struct
    ただしクラスベースのオブジェクト指向ではない。

    struct は継承ができず、委譲を強制する言語設計になっている。
    6

    View Slide

  7. DB アクセスを Repository へ移譲する
    type UseCase interface {
    GetTodos() ([]Todo, error)

    }

    type useCase struct {

    repoitory Repository
    }

    func NewTodoUseCase(repoitory Repository) UseCase {

    return useCase{

    repoitory,

    }

    }

    func (u useCase) GetTodos() ([]Todo, error) {

    return u.repoitory.FindAllTodos()

    }
    7

    View Slide

  8. interface のモック
    Go には interface のモックを生成する仕組みが公式である
    (gomock)。

    DI などと合わせて使えばテストでは委譲先をモックし、

    今テストしたい struct だけをテストすることができる。
    mockgen -source=repository.go -destination=mock/mock_repository.go

    8

    View Slide

  9. mock を使ったテスト
    func TestGetTodos(t *testing.T) {

    mockRet := []model.Todo{{ID: 1, Content: "Todo1"}}

    // mock
    生成
    ctrl := gomock.NewController(t)

    defer ctrl.Finish()

    mock := mock_repository.NewMockRepository(ctrl)

    //
    期待する振る舞いを設定

    mock.EXPECT().FindAllTodos().Return(mockRet, nil)

    //
    注入

    usecase := NewUseCase(mock)

    actual, _ := usecase.GetTodos()

    assert.Equal(t, mockRet, actual)
    }

    9

    View Slide

  10. API のテスト
    10

    View Slide

  11. DB のテスト
    Go に限った話ではないがいくつか方法がある
    mock を使う方法
    比較的簡単だが、DB を使ったときに本当に動くかは分からない。

    ORM を使う場合は生成される SQL を再現しないといけないかも。
    軽量な DB を使う(割愛)
    実際の DB を使う方法
    mock の逆で、初期化と後処理の方法を考える必要がある。
    11

    View Slide

  12. go-txdb
    後処理はテスト中の DB 操作をトランザクションにして、

    テストケースが完了したらロールバックすれば良い。
    go-txdb というライブラリを使うとコネクションを

    Close するだけで Open してからの操作をロールバック

    してくれる。
    12

    View Slide

  13. func TestCreateTodo(t *testing.T) {

    txdb.Register("find_all_todos", "mysql", "dsn")

    db, _ := sql.Open("find_all_todos", "dsn")

    defer db.Close() //
    最後に閉じてロールバック

    repo := NewRepository(db)

    actual, _ := repo.CreateTodo("todo4")

    assert.Equal(t, &model.Todo{ID: 4, Content: "todo4"}, actual)

    todos, _ := repo.FindAllTodos()

    assert.Equal(t, 4, len(todos))

    }

    13

    View Slide