Golang testing 単体テストの書き方

#はじめに
**「Go言語で単体テストってどうやって書くの？？」**

調べてみたので、まとめてみました。

Golangの標準パッケージ`testing`を使用すると、単体テストを書くことができます。

#`testing`
`testing`では、`_test.go`のようにテストファイルを作成します。
テストコードは、`TestXxx`と記載し、Xxxについてのテストを実行します。

テストコードを対応するソースコードと同一のパッケージにすることで、privateな変数や関数を呼び出すことができます。 APIを必要以上公開せずにテストを書くことができます。

#`testify`
標準パッケージの`testing`にはassert関数はありません。
`testify`ライブラリーを使用することで、assert関数を使うことができます。

使用する場合は、下記のようにインストールしてください。

```
$ go get -u -v github.com/stretchr/testify
```

#テストの例
例として`user.go`のテストを、`user_test.go`として作成してみました。

```user.go
package user

type User struct {
  ID int64
  Name String
  Email String
}

func NewUser(id int64, name string, email string) *User {
  return &User{
    ID: id,
    Name: name
    Email: email
  }
}
```

```user_test.go
package user

import "testing"
import "github.com/stretchr/testify/assert"

func TestUser(t *testing.T) {
  var id int64 = 1
  var name string = "Nakata"
  var email string = "nakata@example.com"
  user:= NewUser(id, name, email)

  if user == nil {
    t.Errorf("failed NewUser()")
  }

  assert.Equal(t, id, user.ID)
  assert.Equal(t, name, user.Name)
  assert.Equal(t, email, user.Email)

  t.Logf("user: %p", user)
  t.Logf("user.ID: %d", user.ID)
  t.Logf("user.Name: %s", user.Name)
  t.Logf("user.Email: %s", user.Email)
}
```
#テストの実行

下記のようにコマンドを実行すると、テストを実行できます。

```
$ go test //ログを出さない場合
$ go test -v  //ログを出す場合

PASS
ok  	user	0.013s
```

#階層構造
`t.Run`を使うことで、テストを階層構造にすることができます。
先ほどのサンプルを改良してみましょう。

```user_test.go
package user

import "testing"
import "github.com/stretchr/testify/assert"

func TestUser(t *testing.T) {

  t.Run("success NewUser()", func(t *testing.T){
    var id int64 = 1
    var name string = "Nakata"
    var email string = "nakata@example.com"
    user:= NewUser(id, name, email)

    if user == nil {
      t.Errorf("failed NewUser()")
    }

    assert.Equal(t, id, user.ID)
    assert.Equal(t, name, user.Name)
    assert.Equal(t, email, user.Email)

    t.Logf("user: %p", user)
    t.Logf("user.ID: %d", user.ID)
    t.Logf("user.Name: %s", user.Name)
    t.Logf("user.Email: %s", user.Email)
  })

  t.Run("success NewUser()", func(t *testing.T){
    var id int64 = 2
    var name string = "Suzuki"
    var email string = "suzuki@example.com"
    user:= NewUser(id, name, email)

    if user == nil {
      t.Errorf("failed NewUser()")
    }

    assert.Equal(t, id, user.ID)
    assert.Equal(t, name, user.Name)
    assert.Equal(t, email, user.Email)

    t.Logf("user: %p", user)
    t.Logf("user.ID: %d", user.ID)
    t.Logf("user.Name: %s", user.Name)
    t.Logf("user.Email: %s", user.Email)
  })
}
```

テストを実行してみましょう。

```
$ go test //ログを出さない場合
$ go test -v  //ログを出す場合

PASS
ok  	user	0.015s
```

#ベンチマーク
パフォーマンスの測定をしたい場合、`BenchMarkXxx`と記載し、Xxxについてのパフォーマンスを測定します。
`NewUser()`を測定してみましょう。

```user_test.go
func BenchmarkUser(b *testing.B) {
  var id int64 = 1
  var name string = "Nakata"
  var email string = "nakata@example.com"

  b.ResetTimer()
  for i:= 0; i < b.N; i++ {
    user:= NewUser(id, name, email)
  }
  b.StopTimer()

  if order == nil {
    b.Errorf("failed NewUser()")
  }

}
```

ベンチマークは、オプション`-bench`をつけると実行できます。

```
$ go test -bench .

BenchmarkUser-4   	30000000	        46.6 ns/op
PASS
ok  	user	1.464s
```

さらに詳しい情報を知りたい場合は、`-benchmem`も追加します。

```
$ go test -bench .

BenchmarkUser-4   		30000000	        45.5 ns/op	     128 B/op	       1 allocs/op
PASS
ok  	user	1.429s
```

BenchmarkUser-4の、各列の意味としては下記の感じです。
・関数の実行回数：多いほど良い
・１回の実行にかかった時間[ns/op]：少ないほど良い
・実行ごとに割り当てられたメモリのサイズ[B/op]：少ないほど良い
・１回の実行でメモリアロケーションが行われた回数[allocs/op]：少ないほど良い

#カバレッジ
テストをしていると、カバレッジも知りたくなります。
`-cover`オプションをつけることで、テスト実行結果にカバレッジが表示されます。

```
$ go test -cover .

ok  	user	0.013s	coverage: 100.0% of statements
```

#おわりに
Golangのテストに必要な物を調べて記載してみました。これでテストはできそうですね。
`testify`については、よく使うものなどを追記していきます。