😽

Goのテスト

2023/05/30に公開

Goのテスト

参考:Go言語 プログラミングエッセンス

テストの考え方

一般的に、ソフトウェアにおけるテストには大きく二つの意味があります。

  • 品質を保証するため

    テストが通っていることを他者に明示することにより、そのライブラリの使用者や開発の依頼もとに対して品質の良さをアピールすることです。

  • 安全に壊すため

    一度書き上げたソースコードに対して変更を行う際に、テストがあることによって安心してコードに手を加えられることを指します。「壊す」というと聞こえが悪いのですが、これは悪いことではなく、リファクタリングをする際にエラーを出してくれることで、知らない間に仕様を壊すことなく、安全にリファクタリングできる効果があります。

Goのテストの基本

Goでテストを書くには、_test.goで終わるファイルのソースコードにテストコードを実装します。sample.goがテスト対象のコード、sample_test.goがテストコードです。

sample.go
package hsd

func StringDistance(lhs,rhs string) int {
	return Ditance([]rune(lhs),[]rune(rhs))
}

func Ditance(a,b []rune) int {
	dist := 0
	if len(a) != len(b) {
		return -1
	}

	for i := range a {
		if a[i] != b[i] {
			dist++
		}
	}
	return dist
}
sample_test.go
package hsd

import (
	"testing"
)

func TestStringDistance(t *testing.T) {
	got := StringDistance("foo", "foh")
	want := 2
	if got != want {
		t.Fatalf("expected: %v,got %v",want, got)
	}
}

*後述しますが、このテストは間違っています。

Goのテストは関数名をTestから始め、引数に*testing.Tを持つ関数が実行されます。

テストの結果が失敗したことを明示するには2種類の関数を使い分けます。

  • t.Fatal/t.Fatalf
  • t.Error/t.Errorf

t.Fatalt.Fatalfはその時点でテストを終了します。また、t.Errort.Errorfはエラーを明示したあともテストを続行します。いずれも、fmt.Printfmt.Printfと使い方は同じです。エラーを示す際には、期待値と結果の両方を出すといいでしょう。このケースでは、wantを期待していたがgotを得た、ということを示しています。

さて、このコードは2つの文字列間の距離を得るStringDistance関数へのテストで、文字列foofohの距離gotを得て、期待値wantと比較します。テストを実行してみましょう。

↓実行結果

terminal
go-test % go test
--- FAIL: TestStringDistance (0.00s)
    sample_test.go:11: expected: 2,got 1
FAIL
exit status 1
FAIL    example.com     0.487s

このテストではあえて間違った期待値「2」を書いてあるのでテストは失敗します。正しいテストに修正して実行すると以下のようになります。

sample_test.go
package hsd

import (
	"testing"
)

func TestStringDistance(t *testing.T) {
	got := StringDistance("foo", "foh")
	want := 1
	if got != want {
		t.Fatalf("expected: %v,got %v",want, got)
	}
}

↓実行結果

terminal
go-test % go test
PASS
ok      example.com     0.402s

テストをする際には、パッケージ名の指定に2つの方法があります。

  • テスト対象と同じパッケージ名称でテストコードを書く

    private関数もテストすることができる

  • テスト対象とは別のパッケージ名称でテストコードを書く

    privateな変数や関数にアクセスできないテストを書くことができるため、このパッケージの使用者と同じ実装をサンプルコードとして明示することができます。

Discussion