LoginSignup
26
19

More than 3 years have passed since last update.

Golang testing 単体テストの書き方

Posted at

はじめに

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

カバレッジ

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

$ go test -cover .

ok      user    0.013s  coverage: 100.0% of statements

おわりに

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

26
19
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
26
19