Go
golang

外部テストパッケージの利用ケース #golang

More than 1 year has passed since last update.

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:

スクリーンショット 2016-12-29 8.39.34.png

外部テストパッケージでない通常のパッケージでも記述することは出来ますが、そうするとテスト対象と同じパッケージになってしまうので、godocで表示されるExampleもパッケージプレフィクスなしのコードになってしまいます↓↓

スクリーンショット 2016-12-29 9.17.52.png

これだと利用者がサンプルをそのまま実行できなくなります。外部テストパッケージを利用することでユーザーが外部のパッケージから利用する場合のコードをExampleに出力することができる様になります。

あと、余談ですが、godocの-playオプションつけた場合に、自前パッケージのExampleにもplaygroundが表示されます(外部テストパッケージを使用しないと表示されません)。

スクリーンショット 2016-12-29 8.59.00.png

でも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な関数や変数をテストしたい場合・・・などかな?