Go
golang
testing
TravisCI

はじめての Go 言語 (on Windows) その7

More than 3 years have passed since last update.

これまでの記事の目次

前回の続き。パッケージ化までしたのならテストまで行っちゃおうかと。


テストコードを書く

そもそもテストコードを書く前に実際のコードを書くなんてのは邪道なのですが,今まで「正しく動かす」ことはあまり意識してなかったので。ゴメンペコン。

Go 言語では最初からテスト・フレームワークが同梱されています。最近の言語はみんなそうですよね。テストコードを書くには対象のソースファイルと同じフォルダに *_test.go という名前のファイルを用意します。まぁ,説明するより書いた方が早いですね。


modjulian_test.go

package modjulian

import (
"os"
"testing"
"time"
)

type mjdnTest struct { //test case for DayNumber
in time.Time //input data
out int64 //expected result
}

var mjdnTests []mjdnTest //test cases for DayNumber

func TestMain(m *testing.M) {
//initialization
mjdnTests = []mjdnTest { //test cases for DayNumber
{time.Date(1969, 12, 31, 0, 0, 0, 0, time.UTC), int64(40586)},
{time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), int64(40587)},
{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), int64(57023)},
}

//start test
code := m.Run()

//termination
os.Exit(code)
}

func TestModifiedJulianDayNumber(t *testing.T) {
for _, testCase := range mjdnTests {
result := DayNumber(testCase.in)
if result != testCase.out {
t.Errorf("DayNumber of \"%v\" = %d, want %d.", testCase.in, result, testCase.out)
}
}
}




  1. package にはテスト対象のパッケージを指定します。


  2. import には testing パッケージを含めます。


  3. Test... で始まる関数名がテスト実行用の関数です。引数には t *testing.T を指定します。


  4. TestMain() は特別な関数です。テストの最初に呼び出され, Run() で他のテスト関数(群)をキックします。引数には m *testing.M を指定します。 TestMain() 内で初期化や条件を変えたテストの繰り返しや後始末処理などを行うことができます。

testing パッケージには,他の言語のテスト・フレームワークによくある assertion 関数がありませんFAQ によると


一般的なテストフレームワークにおいて条件・制御・出力機構を持つ専用のミニ言語が用意される傾向がありますが、Go言語にはすでにこれらが備わっています。これらを再び作成するより、我々はGo言語のテストを進めたかったのです。このようにしたことで余計な言語を覚える必要がなくなり、テストを直接的かつ理解しやすくしています。


とあります。テスト駆動型開発の場合,テストコードはそれ自体が設計書として機能しますので,この割り切りは妥当と言えます1。その代わりテストコードを(ドキュメントとして)きちんと書くのは骨が折れますが(笑)

テストコードが書けたので早速動かしてみましょう。テストを行うには test コマンドを使います。また引数に ./... と指定すればカレント・フォルダ以下の全てのテストが対象になります。

C:>go test -v ./...

=== RUN TestModifiedJulianDayNumber
--- PASS: TestModifiedJulianDayNumber (0.00s)
PASS
ok _/C_/pathto/astrocalc/modjulian 0.255s

これは成功例。じゃあ,元のコードを少しいじってわざと失敗させてみましょうか(なんだかなぁ)。

C:\home\spiegel\docs\docs-baldanders\wiki\Draft\golang-src\astrocalc>go test -v ./...

=== RUN TestModifiedJulianDayNumber
--- FAIL: TestModifiedJulianDayNumber (0.00s)
modjulian_test.go:35: DayNumber of "1969-12-31 00:00:00 +0000 UTC" = 40585, want 40586.
FAIL
exit status 1
FAIL _/C_/pathto/astrocalc/modjulian 1.729s

エラーレポートを吐く Errorf() は内部で Fail() を呼び出し,テスト自体は続行します。一方 Errorf() の代わりに Fatalf() を使うと,内部で FailNow() を呼び出しテストを中断します。

Go 言語のテスト・フレームワークでは benchmark や coverage もサポートしてますが,今回は割愛します。


テストの自動化(Continuous Integration)

今回のコードは自動化するほどの規模でもないですが,話のついでに Travis CI で自動化しちゃいましょう。えっと,今回は Travis CI の説明は割愛します。ネットにいくらでも解説が転がってますし。

テストを行うだけなら .travis.yml の記述は超簡単です。


.travis.yml

language: go

go:
- 1.4

script:
- go test -v ./...


実行結果はここを参照して下さい。


ブックマーク


脚注





  1. 私は組み込みエンジニアなので,プログラミングで assert を多用するのは,エンジニアの怠慢だと思ってしまいます。まぁ,ベクタ・テーブルからゴリゴリ書くってのなら別ですが。