tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

【go】golangのtestの書き方 - unit testの基本の基本

はじめに

そこまでgoにはさわれてませんが、
goのtestに関してもう少しまともに勉強しようと思い
まずは基本中の基本からの自分用整理メモです

アジェンダ

  1. goのtestの概要
  2. goのtestのものすごく簡単なサンプル
  3. go testの詳細表示(-vオプション)
  4. go testのskip
  5. go testの実行範囲
    • パッケージ(ディレクトリ)単位で実行
    • 再起的に実行
    • テスト関数の個別実行

その前に

この記事で使うサンプルは以下にあげてあります。
基本的にはそのまま試せば動くと思います。

https://github.com/tweeeety/go-test-sample

1. goのtestの概要

goのtestを行うのはシンプルです。

testにはgo標準パッケージのtestingパッケージを利用します。
goをインストールした時点で使えるので他に準備はいりません。
http://golang.org/pkg/testing/

語弊を恐れず書けばgoでのtestの基本はこれだけです。

  1. go testマンドで実行する
  2. _testというサフィックスのついたファイルを対象にしてテストする
  3. テストファイル内のTestXXXXというプレフィックスのついた関数を対象にテストする
  4. testingパッケージという標準パッケージを使う

2. goのtestのものすごく簡単なサンプル

上述したgoのtestの概要の1〜4のみのサンプルです。
コードで確認したほうがわかりやすいのでさっそく。

簡単なtestのサンプル

mainと文字列を返す関数1つだけのパッケージを用意しました。
それをテストします。

あげてあるのでそのまま試す事もできます。
https://github.com/tweeeety/go-test-sample

構成

構成も簡単ですね。

$ tree 
.
├── main.go
└── sample01
    ├── sample01.go
    └── sample01_test.go
main.go
package main

import (
  "fmt"

  "./sample01"
)

func main() {
  s1 := sample01.HelloWorld("hoge")
  fmt.Println(s1)
}
sample01/sample01.go
package sample01

func HelloWorld(s string) string {
  return "hello world, " + s
}
sample01/sample01_test.go
package sample01

import (
  "testing"
)

func TestHelloWorld(t *testing.T) {
  actual := HelloWorld("hoge")
  expected := "hello world, hoge"
  if actual != expected {
    t.Errorf("actual %v\nwant %v", actual, expected)
  }
}
実行する

実行するときもこれだけです。簡単ですね!

# mainを実行(testではなく通常の実行)
$ go run main.go 
hello world, hoge

# testを実行
$ go test ./sample01/
ok    _/Users/hoge/go-test-sample/sample01 0.005s

簡単なtestのサンプル for failの場合

failのときのサンプルとして、上記のtestをfailにしてみます。

goのtestでは、関数がt.Errort.Fail
呼び出す事でそのテストは失敗したと見なします。

sample01/sample01_test.go をfuga に変更

failさせるにあたって修正するのは
sample01/sample01_test.goの引数の文字列をfugaに変えるだけです。

package sample01

import (
  "testing"
)

func TestHelloWorld(t *testing.T) {
  // ここだけ変更
  actual := HelloWorld("fuga")
  expected := "hello world, hoge"
  if actual != expected {
    // t.Errorfが呼び出されて失敗とみなされる
    t.Errorf("actual %v\nwant %v", actual, expected)
  }
}
実行
$ go test ./sample01
--- FAIL: TestHelloWorld (0.00s)
  sample01_test.go:11: actual hello world, fuga
    want hello world, hoge
FAIL
FAIL  _/Users/hoge/go-test-sample/sample01 0.006s

3. go testの詳細表示(-vオプション)

-vオプションをつける事で詳細を表示できます。
基本的には毎回つけていて良いと思います。

どこかで書く予定ですが、
testingパッケージのt.Log()-vオプションをつけたときしか表示されません。

サンプル
# -v無し
$ go test ./sample01/
ok    _/Users/hoge/go-test-sample/sample01 0.006s

$ go test -v ./sample01/
=== RUN   TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
PASS
ok    _/Users/hoge/go-test-sample/sample01 0.007s

4. go testのskip

testを再帰的に実行するときなどに、
たまにmainはskipしたいなんてこともありますよね。

そんなときは*TのSkipメソッドを呼び出す事でスキップできます。

サンプル

Skipメソッドの使用例です。

  • main_test.go
package main

import (
  "testing"
)

func TestMain(t *testing.T) {
  t.Skip("skip main..")
}
skipしない場合

たとえば、mainのテスト書くつもりもないがskipせずに
再帰的にテストを行おうと思った場合の例です。

no test files と微妙に怒られます

$ go test -v ./...
?     _/Users/hoge/go-test-sample  [no test files]
=== RUN   TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
PASS
ok    _/Users/hoge/go-test-sample/sample01 0.007s
skipする場合

たとえば、mainのテストをskipしつつ 再帰的にテストを行おうと思った場合の例です。

$ go test -v ./...
=== RUN   TestMain
--- SKIP: TestMain (0.00s)
  main_test.go:8: skip main..
PASS
ok    _/Users/hoge/go-test-sample  0.010s
=== RUN   TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
PASS
ok    _/Users/hoge/go-test-sample/sample01 0.009sll

5. go testの実行範囲

go test コマンドは、指定方法により以下の範囲の実行が可能です。

  • パッケージ(ディレクトリ)単位で実行
  • 再起的に実行
  • テスト関数の個別実行
パッケージ(ディレクトリ)単位で実行

これはすでに出ている例ですね

# mainパッケージにあるすべての_test.goを実行
$ go test -v ./
=== RUN   TestMain
--- SKIP: TestMain (0.00s)
  main_test.go:8: skip main..
PASS
ok    _/Users/hoge/go-test-sample  0.007s

# sample01パッケージにあるすべての_test.goを実行
$ go test -v ./sample01/
=== RUN   TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
PASS
ok    _/Users/hoge/go-test-sample/sample01 0.006s
再起的に実行

特定のパッケージ(ディレクトリ)配下を再帰的にテストしたい場合あますよね。
そんなときは...を使うと再帰的にテストできます。

$ go test -v ./...
=== RUN   TestMain
--- SKIP: TestMain (0.00s)
  main_test.go:8: skip main..
PASS
ok    _/Users/hoge/go-test-sample  0.007s
=== RUN   TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
PASS
ok    _/Users/hoge/go-test-sample/sample01 0.008s
テスト関数の個別実行

test修正中に全てのtestをまわしてたら確認が遅くなりますよね。
そんなときは個別に関数を指定することもできます。

# 直接関数を指定してtest
$ go test -run TestHelloWorld
PASS
ok    _/Users/hoge/go-test-sample  0.008s

補足

パッケージのinportを相対パスで行っていますが、これはあくまでもちょろっと試したい時用なのであしからず。
http://tweeeety.hateblo.jp/entry/2018/01/22/231151

おわり

基本中の基本ですが基本は大事ですよね\(^o^)/
testに関しては勉強する事が多いので、これを機に深掘りしたtestについても書こうと思います!...そのうち...