Golangのテストはテーブルドリブンテストで!


はじめに

Golangでテストを書こうとしたときに推奨されてる実装方法としてテーブルドリブンテスト(TableDrivenTests)というものがあります。
このテスト方法はGoの公式のwiki( https://github.com/golang/go/wiki/TableDrivenTests )でも紹介されてるテスト方法です。

テーブルテストとは?

とりあえずにテーブルテスト書いてみます

square.go
package main

// Square 2つのintを受け取りそれらを掛け合わせた物を返す関数
func Square(a, b int) int {
    return a * b
}
square_test.go
package main

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestSquere(t *testing.T) {
    asserts := assert.New(t)
    for _, td := range []struct {
        title  string
        input  []int
        output int
    }{
        {
            title:  "2×3の答えが6になる",
            input:  []int{2, 3},
            output: 6,
        },
        {
            title:  "3×5の答えが15になる",
            input:  []int{3, 5},
            output: 15,
        },
    } {
        t.Run("Square:"+td.title, func(t *testing.T) {
            result := Square(td.input[0], td.input[1])
            asserts.Equal(td.output, result)
        })
    }
}

よくみるテストと違い、

  • name
  • input
  • output

というstructを持った配列をfor文ででまわし様々なテストパターンを確認するというテスト方法です。
このテストの最も大きな利点は複雑な条件のあるテストをテストしやすいという点です。
それは、インプットの値とインプットの値の可読性が高いためどのようなテストパターンを行っていて、どのようなテストパターンをやっていないかを理解しやすいためです。

またこのテーブルドリブンテストで重要なのはinputoutputをわかりやすく記述ことが最も重要ですので、その点はしっかりと注意して実装を行った方がいいと思います。

テストを並行処理する

テストをどんどん書いていくとテスト処理を並行化したいという思いが出てくると思います。
その時はGoのtestingパッケージではt.Parallel()という関数を噛ませてあげることでテストを並行化することができますが、このテーブルテストでやるとバグがおこってしまうそうです。
そのため並行処理を行う時は以下のように書く必要があるそうです
https://github.com/golang/go/wiki/TableDrivenTests#parallel-testing

square_test.go
package main

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestSquere(t *testing.T) {
    t.Parallel() //←追記
    asserts := assert.New(t)
    tests := []struct {
        title  string
        input  []int
        output int
    }{
        {
            title:  "2×3の答えが6になる",
            input:  []int{2, 3},
            output: 6,
        },
    }

    for _, td := range tests {
        td := td //←追記
        t.Run("Square:"+td.title, func(t *testing.T) {
            result := Square(td.input[0], td.input[1])
            asserts.Equal(td.output, result)
        })
    }
}

まとめ

テストの方法には様々な方法がありますが、それぞれのメリットデメリットをしっかり理解してテストコードを書いていきたいですね。