Goのテスト
Goのテスト
テストの考え方
一般的に、ソフトウェアにおけるテストには大きく二つの意味があります。
-
品質を保証するため
テストが通っていることを他者に明示することにより、そのライブラリの使用者や開発の依頼もとに対して品質の良さをアピールすることです。
-
安全に壊すため
一度書き上げたソースコードに対して変更を行う際に、テストがあることによって安心してコードに手を加えられることを指します。「壊す」というと聞こえが悪いのですが、これは悪いことではなく、リファクタリングをする際にエラーを出してくれることで、知らない間に仕様を壊すことなく、安全にリファクタリングできる効果があります。
Goのテストの基本
Goでテストを書くには、_test.goで終わるファイルのソースコードにテストコードを実装します。sample.go
がテスト対象のコード、sample_test.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
}
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.Fatal
とt.Fatalf
はその時点でテストを終了します。また、t.Error
とt.Errorf
はエラーを明示したあともテストを続行します。いずれも、fmt.Print
やfmt.Printf
と使い方は同じです。エラーを示す際には、期待値と結果の両方を出すといいでしょう。このケースでは、want
を期待していたがgot
を得た、ということを示しています。
さて、このコードは2つの文字列間の距離を得るStringDistance
関数へのテストで、文字列foo
とfoh
の距離got
を得て、期待値want
と比較します。テストを実行してみましょう。
↓実行結果
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」を書いてあるのでテストは失敗します。正しいテストに修正して実行すると以下のようになります。
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)
}
}
↓実行結果
go-test % go test
PASS
ok example.com 0.402s
テストをする際には、パッケージ名の指定に2つの方法があります。
-
テスト対象と同じパッケージ名称でテストコードを書く
private
関数もテストすることができる -
テスト対象とは別のパッケージ名称でテストコードを書く
private
な変数や関数にアクセスできないテストを書くことができるため、このパッケージの使用者と同じ実装をサンプルコードとして明示することができます。
Discussion