memoメモ

最近はGo言語関連で。φ(..)メモメモ

Go言語のパッケージのテストでプライベートな関数や変数を呼び出すには

Go言語でパッケージのテストを書く時に、プライベートな関数(隠蔽した関数)や定数を呼び出したいことがたまにあります。 これを実現する方法をメモ。

プライベートって?

パッケージの内容は以下のような構成になっているとします:

  • eg.go
  • eg_test.go

ここで:

eg.go:

package eg

import ()

func foo() string {
	return "foo"
}

という実装があります。関数名のはじまりを小文字fooにし、意図的に関数を外部から隠蔽しています。これでプライベートになります。(というか、基本は小文字で隠蔽し、公開したい場合は大文字にするといった使い方になるはずです)

さて、テストコードから隠蔽したメソッドを呼ぶにはどうしたらいいでしょう。 関数名のはじまりを大文字にし、Fooとして公開(エクスポート)したくなります。が、公開する必要がないのに公開する(またはテストのためだけに公開する)のは設計として問題があります。

解決策:

eg.goと同一パッケージ名(ここではeg)を使用した、export_test.go を追加します。ファイルの名称は自由ですが、Goの基本パッケージ内でよく使われているため、この名称を利用しています(ただし、_test.goをつけてテスト対象のファイルであることを明示する必要があります)。

この中の実装は:

export_test.go:

package eg

var FooA = fooA

とします。これは、eg.go内のfooAの関数ポインタをFooAという公開した変数へ代入しています。 つまり、これで公開したことになります。 もちろん、このコードはテスト専用ですので、APIとして公開されることはありません。

それでは、テストコードを書いてみましょう:

eg_test.go:

package eg_test

import (
	. "."
	"testing"
	)

func TestFunc(t *testing.T) {
	result := Foo()
	if result != "foo" {
		t.Errorf("result = %v, want %v", result, "foo")
	}
}

結果は:

$ go test 
PASS
ok  

となり、ちゃんと呼び出すことができました。

また、同じようにして、内部変数も参照できます:

eg.go:

const (
        max = '\U0010FFFF' // Maximum value
)

export_test.go:

const (
        Max = max
)

おわりに:

っと、書きましたが、内部のテストを細かく書くよりも公開したAPIを利用するテストを充実させ、リファクタリングに対応できるように心がけたいです。

ここで挙げたコードのサンプルを作ってみましたので、参考にどうぞ:

https://gist.github.com/4570162