7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

効率の良さそうなGoのテストを書く

Last updated at Posted at 2019-03-28

普通のテストとテーブルドリブンテストを実際のコードと比較し、その上でテストデータをJSONファイルとして外部ファイルに分離する手順を残しておきます。

1.テーブルドリブン(駆動)テスト

テーブルテストは、テスト内でデータのテーブルを作成し、そのテーブルを介して実行する方法です。

  • 新しいテストケースを追加するための低オーバーヘッド
  • 徹底的なシナリオのテストを簡単にします。すべてのケースを網羅していれば、視覚的に見やすくなります。
  • 報告された問題の再現を簡単にします
  • このパターンをたくさんやりなさい
  • それが成長することが可能であれば、単一のケースでもパターンに従う
    https://about.sourcegraph.com/go/advanced-testing-in-go

下記に通常のテストとテーブルドリブンテストのコードを記載します。

1.1ファイル構造

hoge/
 ├ adder_test.go
 └ adder.go

1.2 実行ファイル

単純に足し算するだけです。

adder.go

package hoge

type Adder struct{}

func (a *Adder) Add(l, r int) int {
	return l + r
}

※テストフレームワーク「testify」のassertを使用しています。

https://github.com/stretchr/testify

1.3 テーブルドリブンでないテストコード

テストを追加する場合はサブテストT.Runメソッド内をさらに記述する必要があります。

※因みに、「t.Parallel()」はt.Runメソッドを並列実行を可能に出来ますが、テストエラーが出た時、論理的エラーなのか人為的なのか判断付かなくなる為、原則使用しないとの事。(多分)

https://about.sourcegraph.com/go/advanced-testing-in-go

adder_test.go
package hoge
import (
	"testing"
	"github.com/stretchr/testify/assert"
)
func TestAdder_Add(t *testing.T) {
	t.Run("加算", func(t *testing.T) {
		// t.Parallel()
		t.Log("1:加算")
		sum := new(Adder).Add(1, 1)
		result := 2
		assert.Equal(t, sum, result)
	})
	t.Run("減算", func(t *testing.T) {
		t.Log("2:減算")
		sum := new(Adder).AddMulti(2, -1)
		result := 1
		assert.Equal(t, sum, result)

	})
}

1.4 テーブルドリブンなテストコード

構造体に設定したデータを元に、テストを回しているので、
テストケースを追加したい時はデータを一行追加ですみます。

adder_test.go

package hoge
import (
	"testing"
	"github.com/stretchr/testify/assert"
)
func TestAdder_Add(t *testing.T) {
	cases := []struct {
		Name                  string // これはただの名前
		Left, Right, Expected int
	}{
		{"加算", 1, 1, 2},
		{"減算", 2, -1, 1},
	}
	for i, tc := range cases {
		t.Logf("%v:%v", i, tc.Name)
		sum := new(Adder).Add(tc.Left, tc.Right)
		assert.Equal(t, sum, tc.Expected)
	}
}

2. テストデータの外部化(fixture)

テーブルドリブンにした上で、テストデータをJSON形式で外に出します。
テストデータ項目が多い場合に良いかと※ 正直これでいいのが微妙

2.1 ファイル構造

テストデータを"testdata"ディレクトリを配置するのには意味があります。

Goはこのtestdataをパッケージとしては見なさないため、様々なテストデータを置くことができます。
https://qiita.com/atotto/items/f6b8c773264a3183a53c

hoge/
 ├ testdata/
 │ └ fixture.json
 ├ adder_test.go
 └ adder.go

2.2 fixture.json

jsonに各関数のごとの領域を設けて、データを設定します。
これだとどの関数に対しどんなテスト、テストデータを使用したのかがわかり、メンテがしやすいと思います。
パッケージに一つのJSONを作るのか、実装ファイル毎にJSONファイルを作るのかどちらがよいか…

fixture.json

[
    {
        "Add":[ テスト対象の関数名
            {
            "name":"加算", テストの名前をつける
            "left":1,
            "right":1,
            "expected":2
            },
            {
            "name":"減算",
            "left":2,
            "right":-1,
            "expected":1
            },
        ],
        "AddMulti" :[{}] 他に関数があればテストデータを追加する
    }
]

2.3 テストコード

JSONデータを使用してテストを行う

adder_test.go

package hoge

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"testing"

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

// 読み込む関数のテストケースを設定
type Fixture struct {
	Add []TestCaseAdd `json:"Add"`
}

// 各関数ごとのテストデータを格納する構造体
type TestCaseAdd struct {
	Name     string `json:"name"`
	Left     int    `json:"left"`
	Right    int    `json:"right"`
	Expected int    `json:"expected"`
}

func DecodeJSON(src string) (fixture []Fixture) {
	// JSON読み込み
	data, err := ioutil.ReadFile(src)
	if err != nil {
		log.Fatal(err)
	}
	// JSONデコード
	if err := json.Unmarshal(data, &fixture); err != nil {
		log.Fatal(err)
	}
	return
}

// テスト実行部
func TestAdder_Add(t *testing.T) {
	// JSON読み込み、デコード
	fixture := DecodeJSON("testdata/fixture.json")
	// 読み込みテストデータを元にテスト実行
	for i, v := range fixture[0].Add {
		t.Logf("%v:%v %v %v %v \n", i, v.Name, v.Left, v.Right, v.Expected)
		sum := new(Adder).Add(v.Left, v.Right)
		assert.Equal(t, sum, v.Expected)
	}
}

実行ファイル内に複数関数がある場合はテスト関数を複数作るのではなく、t.Runメソッドを使用した方が良さそう


// ~ 省略 ~
// テスト実行部
func TestAdder(t *testing.T) {
    // JSON読み込み、デコード
    fixture := DecodeJSON("testdata/fixture.json")

    t.Run("Adder", func(t *testing.T) {
        for i, v := range fixture[0].Adder {
            t.Logf("%v:%v %v %v %v \n", i, v.Name, v.Left, v.Right, v.Expected)
            sum := new(Adder).Add(v.Left, v.Right)
            assert.Equal(t, sum, v.Expected)
        }
    }
    t.Run("AddMulti", func(t *testing.T) {
        for i, v := range fixture[0].AddMulti {
            // 省略
        }
    }
}

素晴らしいエントリ達

7
8
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
7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?