LoginSignup
4
5

More than 5 years have passed since last update.

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

Posted at

これまでの記事の目次

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

テストコードを書く

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

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 を多用するのは,エンジニアの怠慢だと思ってしまいます。まぁ,ベクタ・テーブルからゴリゴリ書くってのなら別ですが。 

4
5
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
4
5