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

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

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

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

### 1.1ファイル構造
```
hoge/
　├ adder_test.go
　└ adder.go
```


### 1.2 実行ファイル
単純に足し算するだけです。  

```go: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


```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 テーブルドリブンなテストコード
構造体に設定したデータを元に、テストを回しているので、  
テストケースを追加したい時はデータを一行追加ですみます。  


```go: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ファイルを作るのかどちらがよいか…

``` json:fixture.json

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

### 2.3 テストコード
JSONデータを使用してテストを行う


```go: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メソッドを使用した方が良さそう

```go

// ～ 省略 ～
// テスト実行部
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 {
            // 省略
        }
    }
}
```

## 素晴らしいエントリ達
- Advanced Testing in Go  
https://about.sourcegraph.com/go/advanced-testing-in-go
- Go 言語 testing チートシート  
https://qiita.com/nirasan/items/b357f0ad9172ab9fa19b
- Goでテストを書く(テストの実装パターン集)  
https://qiita.com/atotto/items/f6b8c773264a3183a53c
- golangでテストを書くならテーブルドリブンテスト  
https://qiita.com/yut-kt/items/5f9eb752f40d4d2a2e97
- Go言語でJSONを扱う  
https://qiita.com/nayuneko/items/2ec20ba69804e8bf7ca3
- JSON-to-Go  
https://mholt.github.io/json-to-go/