LoginSignup
29
14

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-29

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

29
14
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
29
14