- 辻大志郎
- 約 11,800 文字
- 34,100 View
目次
- はじめに
- Tips
- テストがしたい
- テストの失敗をレポートしたい
- テストをスキップしたい
- テストを並列に実施したい
- テストの前処理や後処理を実施したい
- カバレッジを取得したい
- あるディレクトリ配下のテストをすべて実施したい
- 一部のテストケースのみ実施したい
- サブテストの一部のみ実施したい
- テストのキャッシュを削除したい
- テストコードの雛形を楽に作りたい
- 構造体、マップやスライスの比較を実施したい
- APIサーバにアクセスするテストをしたい
- APIサーバのハンドラのテストをしたい
- データ競合をテストで検知したい
- テストデータを置いておきたい
- テストにヘルパー関数を使いたい
- ブラックボックステストをしたい
- 用途(単体・インテグレーション)などによってテストを切り替えたい
- モックを使ってテストをしたい
- ゴルーチンリークを検出したい
- まとめ
- 参考
新しい記事が公開されています → Goのテストをはじめてみよう(2025年版)
2020/08/15更新: 「テストの失敗をレポートしたい」と「サブテストの一部のみ実施したい」の章を追加。
はじめに
TIG の辻です。今回は春の入門祭りということで Go のテストに入門してみよう! という記事です。
書いた背景ですが Go の標準ライブラリのコードリーディング会で testing パッケージにチャレンジしてみましたが、難しすぎてわからん。そもそも Go のテストって何ができるんだっけ? という話になり、基本的な内容をなるべく具体例をまじえながらまとめました。
ざっとどんなことができるんだろう、という index になれば幸いです。
Tips
Go に組み込まれているテストの仕組みの中に、ベンチマークに関するテストと Example テストというサンプルコード用のテストも含まれているのですが、この 2 つは対象外にします。基礎的と思われる内容から順に並べてみました。
- はじめに
- Tips
- テストがしたい
- テストの失敗をレポートしたい
- テストをスキップしたい
- テストを並列に実施したい
- テストの前処理や後処理を実施したい
- カバレッジを取得したい
- あるディレクトリ配下のテストをすべて実施したい
- 一部のテストケースのみ実施したい
- サブテストの一部のみ実施したい
- テストのキャッシュを削除したい
- テストコードの雛形を楽に作りたい
- 構造体、マップやスライスの比較を実施したい
- APIサーバにアクセスするテストをしたい
- APIサーバのハンドラのテストをしたい
- データ競合をテストで検知したい
- テストデータを置いておきたい
- テストにヘルパー関数を使いたい
- ブラックボックステストをしたい
- 用途(単体・インテグレーションなど)によってテストを切り替えたい
- モックを使ってテストをしたい
- ゴルーチンリークを検出したい
- まとめ
- 参考
テストがしたい
Goのテストは go test コマンドを用いてテストを実施します。テストを実施する関数の命名は以下のような形式でなければなりません。
func TestXxx(*testing.T) |
TestXxx は Testxxx ではダメです。Test_xxx という関数名であれば問題ありません。
テストファイルは xxxx_test.go といった命名である必要があります。このファイルはビルド時には除かれます。簡単なテストを試してみます。
package main |
https://play.golang.org/p/XYrpmljtPrW
go test -v |
上記のテストは TableDrivenTest とサブテストを組み合わせています。どちらも現場でよく使われます。サブテストを用いると各テストごとに結果がわかるようになります。 TableDrivenTest はさまざまな Input/Output パターンを網羅するのに便利です。上記のテストをシンプルに書き直すと以下のようになります。
func add(a, b int) int { |
https://play.golang.org/p/-TQObdTOqPA
go test -v |
テストの失敗をレポートしたい
Go はテストのアサーションを提供していません。理由は公式のFAQで紹介されています。先程の TestAddc 関数のように、テストが失敗したことを開発者が自ら実装する必要があります。失敗したことを示すには T.Error (T.Errorf) や T.Fatal (T.Fatalf) を用いることができます。
T.Fatal を用いると T.Fatal が実行された以降のテストは呼び出されずに終了します。テストが失敗したことを示すには T.Error を使い、テストの初期化など、処理が失敗するとその後のテストが無意味になる場合は T.Fatal を用いると良いでしょう。以下のように t.Fatalf を用いた場合は、その後のテストの処理 t.Log("after add() ...") が呼び出されていないことが分かります。defer や T.Cleanup といった後処理は呼び出されます。
func TestAdd(t *testing.T) { |
go test -v |
https://play.golang.org/p/2NZxz45zGD7
なお t.Fatalf と似たような関数名でログ出力してアプリケーションを終了する log パッケージの Fatalf という関数があります。log.Fatalf のGo Docにもあるように log.Fatalf は defer といった後処理を呼び出さずに即座に os.Exit(1) でアプリケーションが終了します。t.Fatalf と log.Fatalf を混乱しないように注意しましょう。
https://play.golang.org/p/ADm5xU7gsfm
テストをスキップしたい
時間がかかるテストなど、自動テストなどではテストをスキップしたい場合があるかもしれません。次のようなある条件の場合は処理にめちゃくちゃ時間がかかる例を考えてみます。
func add(a, b int) int { |
このテストをスキップしたいとします。その場合は func (c *T) Skip(args ...interface{}) というメソッドを用いることでスキップできます。
テストコードに以下を追加します。
b: 2, |
go test -v -short |
テストがスキップされていることが分かります。さらっと testing.Short() という関数も用いましたが Short() は testing パッケージに含まれている関数で、-short フラグがセットされていると true になります。そのためテストを実施するときに -short というフラグを付与したときだけテストがスキップされる、そうでないときはスキップされずテストが実施される、というように使い分けることができます。
標準パッケージでもテストのスキップが実装されているのを色々見ることができます。以下は io/ioutil/ioutil_test.go からの抜粋です。特定の条件を満たす場合にテストをスキップするように実装されています。
func TestReadOnlyWriteFile(t *testing.T) { |
テストを並列に実施したい
以下のような謎に sleep する実装があるとします。テストを並列に実施したいとします。
func add(a, b int) int { |
並列化せず、逐次サブテストを実施すると 3 + 5 + 7 = 15 から約 15 秒テスト実施に時間がかかります。
func Test_add(t *testing.T) { |
go test -v |
並列にテストを実施するには func (t *T) Parallel() メソッドを用いることができます。上記のテストに tt := tt と t.Parallel() を追記します。
for _, tt := range tests { |
上記の実装を用いてテストをします。するとテストが並列に実施されていることが分かります。結果としてテストにかかった時間は約 7 秒になりました。
go test -v |
ループ時に割り当てているローカル変数 tt を捕捉することは重要です。Go ではループで用いられる変数は同じアドレスを使います。
func main() { |
// 変数のアドレスがすべて同じアドレスを参照している |
https://play.golang.org/p/_tSALjq8yZ6
そのため、テストを並列に実行するときは tt := tt などとして変数をシャドウイングし、並列で実行しているテストに影響がないようにする必要があります。
ただし、テストケース自体がそもそも並列に実行できない場合、例えばデータベース上のテーブルへの UPDATE や INSERT が発生し、テストケースで競合する場合、テストを並列に実行できないため、注意が必要です。
テストの前処理や後処理を実施したい
テストをしていると、前処理や後処理をしたい場合があると思います。主な例の 1 つとしてデータベースの処理化があるでしょう。テストを実施する前処理としてあるデータを INSERT しておいて、テスト実施後に対象のテーブルのデータを削除する、といったものです。
そのような共通的な前処理や後処理を実施したい場合は…
func TestMain(m *testing.M) |
関数を用いることができます。
例を見てみます。
func f() { |
以下のように TestMain を用いてテストを制御するとテストの前後(今回の場合は f() の前後)に処理を実行できます。m.Run() の実行結果を取得して os.Exit() するのが慣用的です。
func Test_f(t *testing.T) { |
go test |
参考までに google/trillian という OSS で TestMain がどのように使われているか確認してみます。以下はテスト用のデータベースを openTestDBOrDie() として作成し、テスト終了後に done(context.Background()) という処理を実施しています。
func TestMain(m *testing.M) { |
https://github.com/google/trillian/blob/master/storage/postgres/storage_test.go#L168-L181
- 後処理には
T.Cleanupが便利
Go1.14 でテスト時に生成したリソースを便利に後処理できる関数が登場しました。T.Cleanup です。
func (c *T) Cleanup(f func()) |
リソースの後処理という意味では defer を用いてクリーンアップできるので、今までとあまり変わらないのでは? と思うかもしれません。T.Cleanup を便利に使えることが実感できるシーンの 1 つとして、テストに必要な前処理をテストとは別の関数で実施している場合があります。
簡単な例ですが、テストの前準備としてテスト用のファイルを生成する必要があったとして、テスト終了後に削除したい場合、以下のような実装が考えられます。TempFile 関数ではリソースをクローズする処理を呼び出し元に返却する必要があり、呼び出し元で後処理として teardown 関数を呼び出すことになります。
func TempFile(t *testing.T, content []byte) (name string, teardown func()) { |
呼び出し元の処理です。
file, delete := testutil.TempFile(t, nil /* something */) |
T.Cleanup を用いると前処理を実施する関数内でリソースの後処理が実施できるようになります。TempFile 関数の例であれば、以下のように t.Cleanup を用いることができます。関数から return したタイミングで呼び出される defer とは異なり、テストが完了したタイミングで Cleanup 処理が呼び出されます。
func TempFile(t *testing.T, content []byte) string { |
ファイルでなくても、テスト用のデータベースや HTTP サーバの teardown など、テストに必要なリソースの teardown 処理を同じ関数内に記述でき、テストコードが簡潔になります。
カバレッジを取得したい
標準パッケージのカバレッジを取得してみます。今回は io パッケージ( ioutil も含む)のテストのカバレッジを取得することにします。カバレッジは go test コマンドの引数として -covermode=count を付与すれば取得できます。 -coverprofile=c.out で結果をファイルに保存します。
Go のコードは /Go/src にあるものとします。以下のコマンドを実行してカバレッジを取得します。
go test io/... -covermode=count -coverprofile=c.out |
以下のコマンドで上記の出力ファイルを簡単に HTML で可視化できます。
go tool cover -html=c.out -o coverage.html |
このような感じでどのパスが通っていないか確認できます。
あるディレクトリ配下のテストをすべて実施したい
たとえば標準パッケージの例だと、io パッケージはテスト対象に含めるが、その他のパッケージはテスト対象に含めない…といった要領です。これはテストのコマンドというよりはパッケージのコマンドになります。以下のように ... の文字列を用いてワイルドカードとしてテスト対象のファイルを選択できます。詳細は go help packages とすることで確認できます。
以下の場合は io パッケージに含まれるすべてのテストを実行します。
go test io/... |
同様に io パッケージに含まれる ioutil パッケージのみテストしたい場合は以下のようになります。
go test -v io/ioutil/... |
一部のテストケースのみ実施したい
実行対象のテストを抽出するには -run フラグを用いることできます。以下のように正規表現を用いて、一致するテストのみを実行できます。
-run regexp |
例えば io パッケージで Pipe に関するテストのみ実行したいとしましょう。Pipe に関するテストはテスト関数に Pipe の文字列が含まれるものとします。以下のように実行するとテストの関数名に Pipe が含まれるテストのみ実行されていることが分かります。
go test -v io/... -run Pipe |
サブテストの一部のみ実施したい
TableDrivenTest でテストを実装していると、ある 1 つのテストケースで複数のサブテストを実装します。サブテストの一部のみテストを実行したい場合はテストケースを絞り込むときと同様に -run フラグを用いることができます。
func TestAdd(t *testing.T) { |
TestAdd の normal のサブテストのみ実行されていることが分かります。サブテストがたくさんある場合で、一部のみ実行したい場合に便利です。
go test -v -run Add/mal |
テストのキャッシュを削除したい
Go でテストをしていると以下のように cached の文字列を見ることがあると思います。テストのキャッシュを削除するにはどのようにすれば良いでしょうか。
go test io/... -run Pipe |
テストのキャッシュは Go1.10 から組み込まれました。テストのキャッシュの詳細は go1.10#test が詳しいです。キャッシュ使わない場合はを明示的に -count=1 と指定すればよいです。-count=1 と明示的に指定するとテストはキャッシュされなくなります。
go test io/... -run Pipe -count=1 |
また go clean -cache を用いてもビルドキャッシュ全体を削除でき、テストのキャッシュも削除できます。
テストコードの雛形を楽に作りたい
Tips ですが IDE などの機能を使うと少し便利になるかもしれません。例えば GoLand の機能を用いるとデフォルトで TableDrivenTest の雛形を生成してくれます。詳細は割愛しますが VS Code を用いて開発する場合でも同様に雛形を生成できます。
構造体、マップやスライスの比較を実施したい
map のキーと値が一致しているかどうか確認するようなテストがしたいとしましょう。map や slice は spec#Comparison_operators にもあるように比較演算子を用いて比較できません。map に含まれるキーと値が同じものを含んでいるかどうかはループを回して確認しないといけないのでしょうか。
reflect パッケージに含まれる reflect.DeepEqual を用いると map のような比較演算子で比較できないオブジェクトの比較ができます。
net/http パッケージにある request_test.go の中でクエリパラメータなどに用いられる map (map[string][]string 型)は以下のように比較して同値かどうかテストしています。
wantForm := url.Values{ |
https://github.com/golang/go/blob/master/src/net/http/request_test.go#L210-L219
Go は言語自体にテスティングフレームワークを提供してません。Where is my favorite helper function for testing? に理由が記載されていますが、端的に言うと開発者が適切にエラーハンドリングすることと適切なエラーメッセージの出力を実装することが重要であるためです。
とはいえ薄い便利なツールを使いたくなるシチュエーションはあるのではないでしょうか。reflect.DeepEqual を使った同値チェックは google/go-cmp を使うとより便利にテストができるので、私は reflect.DeepEqual の代わりとして google/go-cmp を用いることが多いです。
go-cmp のリポジトリにある構造体を比較するサンプルの例を見てみます。以下のような net.IP 型や time.Time 型をフィールドに持つ構造体の比較です。
type ( |
この構造体が同値かどうかテストしてみます。上記の実装の通り MakeGatewayInfo は異なる 2 つの変数 x と y を返す関数です。これを go-cmp と reflect.DeepEqual のそれぞれを用いて比較してみます。
func TestMakeGatewayInfoDeepEqual(t *testing.T) { |
実行すると以下のような結果を得ることができます。
go test example_test.go |
go-cmp の結果は何が同値で、何が同値でなかったか、同値でなかったときは取得した値と想定する値は何か明示的にわかるのがよいです。他にもオプションで条件をカスタマイズできます。(やりすぎ注意)
社内でも stretchr/testify/assert を使う勢なども見かけます。Go のテスティングフレームに関する話は好みが分かれるところだと思うので、深くは触れません。
APIサーバにアクセスするテストをしたい
net/http/httptest を用いると簡単にテスト用のモックサーバをたてることができます。リクエストに対して Hello, client というレスポンスを返却する API を用いた、API クライアントの視点で単体テストを実施する例を考えてみます。
httptest.NewServer にハンドラを渡すことでハンドラの振る舞いをするローカルの HTTP サーバを提供してくれます。デフォルトだとこのテスト用の HTTP サーバは 127.0.0.1:0 で起動します。ポートが 0 にバインドされていますが、これは空いている任意ポートを割り当てます。なお、ブラウザからデバッグしたいなどの用途として httptest.serve フラグを渡すこともできます。-httptest.serve=127.0.0.1:18888 などとしてフラグを指定した場合、HTTP サーバはブロックされます。
func TestX(t *testing.T) { |
https://play.golang.org/p/i1Dz5alS4hQ
実際にはテストで用いる構造体のフィールドに URL を保持できるようにして httptest で起動させたモックサーバの URL に切り替えるなどことが多いのではないか、と思います。
func setup() (client *Client, mux *http.ServeMux, serverURL string, teardown func()) { |
https://github.com/google/go-github/blob/master/github/github_test.go#L31-L64
APIサーバのハンドラのテストをしたい
上記の「APIサーバにアクセスするテストをしたい」の項目では httptest.NewServer でモックサーバを立てて、API クライアントという視点でリクエストを発行してテストをしました。今度は API サーバを提供する視点から、ハンドラの単体テストを実施する場合を考えてみます。httptest.NewRequest を用いると、ハンドラの単体テストを簡潔に実施できます。以下のようなハンドラを考えてみます。
func helloHandler(w http.ResponseWriter, r *http.Request) { |
このハンドラに対して GET リクエストしたときに hello world! というレスポンスが得られるかどうかテストします。httptest.NewRequest はテストのためのリクエストを生成するのに便利です。httptest.NewRequest で生成した *http.Request をハンドラに渡すことができます。また httptest.NewRecorder() で生成できる *httptest.ResponseRecorder を用いるとハンドラのレスポンスを記録できます。
これらを用いて、以下のようにハンドラから想定のレスポンスが得られるかどうかテストできます。
import ( |
go test -v |
httptest.NewRequest() や httptest.NewRecorder() を用いることで、Echo や go-swagger といった Web フレームワークを用いて構築しているハンドラも同様にテストできます。
ちなみにリクエストは http.NewRequestWithContext (http.NewRequest) でも生成できるのは? と思うかもしれません。何が異なるかというと、httptest.NewRequest は http.Handler に適しているリクエストを返却します。一方で http.NewRequestWithContext (http.NewRequest) は Client.Do や Transport.RoundTrip に適しているリクエストを返却します。そのため、単体テストの用途で直接ハンドラにリクエストを渡す場合は httptest.NewRequest を用いることになります。
データ競合をテストで検知したい
テストでレースコンディションを検知したい場合はどうすればよいでしょうか。レースコンディションが起きていないかどうか確認するのに -race フラグが役に立ちます。
公式の The Go Blog の例を引用します。
package main |
上記の実装は異なるゴルーチンから変数 t の読み書きをしていて、レースコンディションを引き起こします。これは -race をつけることで確認できます。-race は go test 以外にも以下のように使うことができます。
go test -race mypkg // test the package |
go test -v -race race_test.go |
上記のようにレースコンディションが発生している場合は、テストが FAIL になります。
-race をつけてテストすると多少実行に時間がかかりますが、想定しないレースコンディションが発生して、テストが FAIL になるとデバッグが大変です。テストの際にデフォルトで -race をつけておくのも1つの方法です。
テストデータを置いておきたい
テストの Input や Output になるファイルを testdata という名前のディレクトリに置いておくことができます。testdata ディレクトリに含まれるファイルはテストのときのみ用いられます。標準パッケージの中でたくさん用いられていますが、image パッケージの例を上げると https://github.com/golang/go/tree/master/src/image/testdata といったものです。
テストにヘルパー関数を使いたい
複数のテストで用いるような共通の関数をヘルパー関数として実装する場合があると思います。テスト用のヘルパー関数の特徴は以下です。
- ヘルパー関数はエラーを返さない
*testing.Tを受け取ってテストを落とす- Go 1.9 からは
T.Helperを使って情報を補足する
Go Friday 傑作選 より引用
テスト用のヘルパー関数は以下のように実装されます。
func mustUrlParse(t *testing.T, s string) *url.URL { |
ヘルパー関数に t.Helper() を付与すると何が嬉しいのでしょうか。上記のヘルパー関数 mustUrlParse を用いたテストを実施してみて確認してみます。
func TestX(t *testing.T) { |
- ヘルパー関数から
t.Helper()を除いた場合
go test -v |
- ヘルパー関数に
t.Helper()がある場合
go test -v |
上記の 2 通りの結果を比較すると FAIL になった行が何行目を指しているのか、が変わっています。
// t.Helper() がない場合 |
t.Helper() を付けた場合は t.Run() 内の _ = mustUrlParse(t, tt.args.str) の行を指しています。付けなかった場合はヘルパー関数でエラーになった t.Fatal(err) の行を指しています。t.Helper() を付けた場合のほうが、テストケース内のどの行で失敗したか分かりやすくなり、エラーの原因を探りやすくなるのではないでしょうか。
ブラックボックステストをしたい
あるパッケージ mypkg があって以下のようなファイル構成になっているとします。ブラックボックステストなどを実施するときにパッケージ名を分けたい、などのニーズがあるかもしれません(そんなに多くは無いと思いますが)。Go ではディレクトリ名とパッケージ名は同じである必要があるため、異なるパッケージ名のファイルを作ることはできません。しかし例外的に mypkg_test という、mypkg に _test を付与したパッケージ名は用いることができます。Test packages にもあるように Go では _test の接尾語をもつパッケージ名は別のパッケージとして扱われます。パッケージ名を mypkg と mypkg_test とで分けることでテスト実施時に mypkg の非公開の関数を呼び出すことはできなくなります。似たような議論を Proper package naming for testing with the Go language で見ることができます。
具体的には以下のようなファイル構成になっていて x.go と x_test.go で別のパッケージ名とできます。
mypkg |
package mypkg |
// mypkg ディレクトリにあるテストファイルでも _test を付けたパッケージ名を混在できる |
用途(単体・インテグレーション)などによってテストを切り替えたい
テストは単体テストの他、インテグレーションテストなど用途によっていくつか存在します。用途によって実行するテストを分けるにはどのようにすれば良いでしょうか。ビルドタグ を用いる方法やディレクトリを分ける方法があります。
- ビルドタグを用いる方法
以下のようにビルドタグ // +build integration を付与します。このようにすると go test -tags=integration とタグを付けたときのみテストが実行されるようになります。タグがない場合はテスト対象に含まれません。
// +build integration |
OSS から具体例をあげると以下のようなものです。以下の例はテストのパッケージ名に _test をつけることでテストであることを明示的に分けています。テストで用いる関数は、テスト内でインポートしています。
// Copyright 2019 Google Inc. All Rights Reserved. |
https://github.com/google/mtail/blob/master/internal/mtail/new_prog_integration_test.go#L1-L44
- ディレクトリを分ける
シンプルながら、インテグレーションテストは integration などのディレクトリに対象のテストケースを含めることが多いのではないでしょうか。その integration パッケージと // +build integration のビルドタグを組み合わせる方法も考えられます。OSS だと以下のようなイメージです。
google/go-github
モックを使ってテストをしたい
外部サービスに依存する実装のテストをするときに、外部サービスをモックしたい場合があります。特に外部の API を実行した結果に依存する場合、API のレートリミットやテストの結果が外部 API の仕様変更などに引きづられ、テスト結果が不安定になります。どのようにすればモック化できるでしょうか。方法はいくつかありますが、基本的な考えはインターフェースを満たす実装を、本物の実装とモックで切り替えていくことになります。
データベースアクセスする処理をモックする簡単な疑似コードを見てみます。データベースからユーザの検索と作成をします。インターフェースのメソッドは以下のようにあるとします。
type DataStore interface { |
type User struct { |
通常の実装は以下のような感じを想定します。ポイントは DataStore のインターフェースをファクトリ関数である NewDataStore で返すところです。
type UserDataStore struct { |
あるテストをする際に、このデータベースに依存する実装をモックする例は以下のようになります。インターフェースを満たす、モックオブジェクトに想定の結果が取得できるメソッドを実施します。
type MockUserDataStore struct{} |
このモックしたオブジェクトをテストで用いる場合は以下のようになります。
func TestXXX(t *testing.T) { |
上記のようにして外部依存する処理をモックできます。モックの struct やメソッドの実装が手間なので gomock を使うこともあるかもしれません(gomock に関して詳細は説明を割愛します)。
インターフェースを用いたテストに関しては Golangにおけるinterfaceをつかったテスト技法 が詳しいです。またインターフェースの埋め込みを用いたモックに関しては Golangにおけるinterfaceをつかったテストで Mock を書く技法 が詳しいです。
余談ですが、テストがモックに依存しすぎるとなんのためのテストをしているか分からなくなったり、リファクタリングのときに辛かったりするので、モックを使ったテストは用法用量に注意です。
ゴルーチンリークを検出したい
ゴルーチンがリークするような実装になっていることをテスト時に確認したいかもしれません。公式のツールチェーンには含まれていませんが、goleak はゴルーチンリークの検出に役に立ちます。このツールを用いるとテスト時にゴルーチンがリークしている場合にテストが FAIL になります。
簡単な例で確認してみます。
func X(send chan struct{}) { |
func TestDoLeak(t *testing.T) { |
結果は以下のようになります。想定通り leak のテストが FAIL になっていることが分かります。
go test -v |
https://play.golang.org/p/wWGWe8_S0WN
まとめ
テストに関する Go の基本的な機能のまとめてみました。なるべく使い方のイメージがわかるように具体的な例を多く載せました。参考にした素晴らしいドキュメントの URL は以下に記載しています。合わせてみてみてください。
参考
- https://blog.golang.org/race-detector
- https://github.com/golang/go/wiki/LearnTesting
- https://golang.org/cmd/cover/
- https://golang.org/cmd/go/
- https://golang.org/doc/articles/race_detector.html
- https://golang.org/pkg/cmd/go/internal/test/
- https://golang.org/pkg/testing
- https://talks.godoc.org/github.com/tcnksm/talks/2016/12/golang-tokyo/golang-tokyo.slide
- https://talks.golang.org/2014/testing.slide