今川館

都内勤務の地味OLです

Goでユニットテストの書き方① 基礎編

testing.Tのポインタを引数に取る関数を定義する

ベンチマークテストのことを先に調べてからユニットテストを調べているのもアレだが、Goでユニットテストを書くときはtesting.Tのポインタを引数に取る関数を定義すれば良いらしい。

assertなんちゃら といったメソッドは無い: t.Error(), t.Errorf()を使う

PythonJavaユニットテストライブラリに慣れた身としては、まずアサーションの実行には「Error」や「Errorf」という名前のメソッドを使うルールに混乱した。

ちゃんと「Fail」というメソッドもあるので、最初こっちを使うのだろうと思ったが、どうやらこれはPythonのunittest.TestCase.fail()に相当する(=とにかくテストを失敗させるメソッド、アサーション無し)もののようだ。

簡単な例を示すとこんな感じ。

[foo/main_test.go]

package foo

import "testing"

func TestFoo(t *testing.T){
	var result int
	var err error
	result, err = Foo("1")
	if result, error = Foo("1"); err != nil {
		t.Errorf("処理に失敗しました: %v", err)
	}

	if result != 10 {
		t.Errorf("結果が不正です: %v != 10", result)
	}
}

非公開モジュールのテストをするためにアプリケーションコードと同じパッケージに所属させる

小文字で名前が始まるモジュールはパッケージの外からアクセスできないので、それらのテストを書く場合はテスト対象と同じパッケージを宣言する。

ひとつのテストケースの中で複数のアサーションをやっても別々に出力してくれる

感心したのが、testing.Tはt.Errorfを同じ関数(=テストケース)の中で複数出力しても処理は続行される点だ。

Pythonならば、assertなんちゃらメソッドを呼んで期待値と結果が異なれば、即座にそのテストケースは打ち切りになる。

import unittest

class FooTest(unittest.TestCase):
    def test(self):
        self.assertEqual(1, 0)  # ここでテスト失敗、処理は打ち切られる。
        self.assertEqual(2, 2)  # ここは通らない

しかし、Goの場合はt.Error, t.Errorfが標準出力にテストの失敗を出してくれるだけで、処理は続行される。

import "testing"

func TestFoo(t *testing.T){
	if 1 != 0 {
		t.Error("結果が不正です: 1 != 0") // ここで処理は止まらない
	}

	if 2 != 1 {
		t.Error("結果が不正です: 2 != 1") // こっちもテストしてくれる
	}
}

[処理結果]

--- FAIL: TestFoo (0.00s)
	sample_test.go:7: 結果が不正です: 1 != 0
	sample_test.go:11: 結果が不正です: 2 != 1
FAIL
FAIL	github.com/oyakata/tokyo/err	0.007s

このように、ちゃんとTestFooの中で別々の検証と判るよう整形してくれることに感銘を受けた。