go listの、-fフラグで指定できるstruct fieldに
XTestGoFiles []string // パッケージ外の_test.goファイル
XTestImports []string // XTestGoFilesからのインポート
というものがあるのに気付いて、「これなんだろ?」と思って調べました。
テストコードのパッケージをxxx_test
という様にサフィックス_test
くっつけておくと外部テストパッケージ(external test package)というものになるらしい。
例
src
└── hoge
├── hoge.go
└── hoge_test.go
hoge.go:
package hoge
// Hoge returns hoge!!
func Hoge() string {
return "hoge!!"
}
hoge_test.go:
package hoge_test
import (
"hoge"
"testing"
)
func TestHoge(t *testing.T) {
if hoge.Hoge() != "hoge!!" {
t.Fatalf("expected:hoge!! but was:%v", hoge.Hoge())
}
}
通常パッケージディレクトリ内に複数パッケージを宣言することはできないですが、外部テストパッケージ{package name}_test
は例外で、そこに書かれたテストはhogeパッケージとは別のパッケージ扱いになるとのこと。
↑の例でも、hoge_testはhogeパッケージと同じディレクトリにありながら、hogeパッケージの関数を呼ぶときはhogeをimportしてパッケージプレフィクス付きで関数呼ぶ必要があります。
検索してもあまり情報ひっかからないのでマイナーな機能なのかな?と思っていたのですが、標準パッケージのコード見てるとめっちゃ使われてました。
数えてみました↓↓
通常パッケージの_test.go
ファイル: 317
外部テストパッケージの_test.go
ファイル: 234
どういうときに使用する?
Exampleテスト
FYI: https://golang.org/pkg/testing/#hdr-Examples
テストコードでExampleXxxという関数を書いておくと、標準出力をassertionするテストと、godocでのExample表示を同時に行ってくれます。
Exampleテストを記述するときには基本的に外部テストパッケージを使用します。
例:
package hoge_test
import (
"fmt"
"hoge"
"testing"
)
func ExampleHoge() {
fmt.Println(hoge.Hoge())
// Output:
// hoge!!
}
godoc:
外部テストパッケージでない通常のパッケージでも記述することは出来ますが、そうするとテスト対象と同じパッケージになってしまうので、godocで表示されるExampleもパッケージプレフィクスなしのコードになってしまいます↓↓
これだと利用者がサンプルをそのまま実行できなくなります。外部テストパッケージを利用することでユーザーが外部のパッケージから利用する場合のコードをExampleに出力することができる様になります。
あと、余談ですが、godocの-play
オプションつけた場合に、自前パッケージのExampleにもplaygroundが表示されます(外部テストパッケージを使用しないと表示されません)。
でもRunするとエラーになっちゃいました・・・(´・ω・`)
これ実行出来ている人いたら教えてくださいm(_ _)m
他のパッケージから使用されるケースのテスト
これは必須ではないのですが、標準ライブラリの外部テストパッケージの使われ方を見ていて自分が感じたことです。
たとえば自分のパッケージでencoding/jsonのMarshaller/Unmarshallerインターフェースを実装している場合など、外部のパッケージ経由で呼び出されるケースのテストで使用します。
hoge.go:
type Fuga struct {
Value int
}
func (f Fuga) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`{"value":%d}`, f.Value)), nil
}
hoge_test.go:
func TestFugaMarshal(t *testing.T) {
fuga := &hoge.Fuga{999}
jsn, err := json.Marshal(fuga)
if err != nil {
t.Fatal(err.Error())
}
if string(jsn) != `{"value":999}` {
t.Fatalf(`expected: {"value":999}, but was:%s`, string(jsn))
}
}
外部テストパッケージを使用することで、hogeパッケージがjson/encodingパッケージに依存しなくなります。
↑の例ではもともと依存関係がありませんでしたが、たとえばhogeパッケージに依存するパッケージpiyoがあったとして、piyoパッケージ経由でhogeが呼び出されるテストも記述することが出来ます。
通常パッケージのテストだと循環依存になって実行することが出来ません。
最後に
外部テストパッケージの利用方針に関して、公式ドキュメントの類があまり見当たりませんでした。
どなたか情報ありましたら教えてくださいm(_ _)m
あと、逆に通常テストパッケージを利用する意義が自分の中で曖昧になりました。。
テストは全部外部テストパッケージでいいのでは・・
unexportedな関数や変数をテストしたい場合・・・などかな?