Goのテストケースを実装した際に、testifyパッケージのrequireとasserを使ったのですが、あまり違いがわからなかったことありませんか?
今回は、Goのテストコードでよく使われるtestify/assert
パッケージとtestify/require
パッケージの違いと、それぞれのメソッドについて調べてみようと思います。
assertとrequireはどのような役割をしているのか
この二つは、testifyパッケージに入っているテスト用ツールです。
違いは以下のようになっています。
パッケージ名 | 違い |
---|---|
assert | テストが失敗しても実行し続ける |
require | テストが失敗したら即時終了 |
主な違いはテストが失敗しても実行し続けるか終了するかの違いになっています。
インストール
下のコマンドで、go.modに依存関係を追加してください。
go get github.com/stretchr/testify
assertとrequireの使い方
assertとrequireは基本的の同じメソッドを持っていて、主なメソッド名と機能は下のようになっています。
値と型の比較
メソッド | 機能 |
---|---|
Equal | 値を比較 |
EqualValues | 型関係なしに値が同じか比較 |
Exactly | 型があっているかどうかまで厳密に比較 |
IsType | 同じ型か比較 |
Equal
Equal()
では、値の確認が行えます。
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
assert.Equal(t, 123, 123) //-> Success
assert.Equal(t, 123, 321) //-> Fail
EqualValues
EqualValues()
では、型関係なしに値が同じか確認できます。
例えば下のようにuint32とint32のように、整数ではあるけれども実際には型が異なっていても、値が同じか確認することができます。
assert.EqualValues(t, uint32(123), int32(123)) //-> Success
Exactly
Equal()
の厳密版のようなメソッドで、型があっているかどうかまで厳密に比較してくれます。
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
assert.Exactly(t, int32(123), int64(123))//-> Success
IsType
型が同じかどうか比較してくれます。
type A struct{
a sting
}
type B struct{
b stirng
}
a := A("1")
b := B("1")
a2 := A("2")
assert.IsType(t, a, a2) //-> Success
assert.IsType(t, a, b) //-> Fail
リストと要素の比較
メソッド | 機能 |
---|---|
ElementMatch | リストの比較 |
Len | スライスの要素数を検証 |
Contains | 含まれるかどうか検証 |
NotContains | 含まれないことを検証 |
ElementMatch
listの比較が行えます。
返ってきたlistが思い通りの結果かどうかの確認に使用できます。
func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool)
assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) //-> Fail
Len
スライスの要素数を検証できます。
スライスを返すメソッドで、想定されている数の要素数が返ってきているかを確認します。
myslice := make([]int,3)
assert.Len(t, mySlice, 3) //-> Success
assert.Len(t, mySlice, 4) //-> Fail
Contains
スライスやmap等で特定のものが含まれているかを確認できます。
a.Contains(["Hello", "World"], "World") //-> Success
NotContains
Containsとは逆で、含まれていないかを確認できます。
a.NotContains("Hello World", "Earth") //-> Success
JSONの比較
メソッド | 機能 |
---|---|
JSONEq | JSONが同じかどうか比較 |
JSONeq
JSONが同じかどうか比較できます。
assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
エラーの検証
メソッド | 機能 |
---|---|
EqualError | 返されたエラーが同じかどうか比較 |
Error | エラーが返されるか検証 |
NoError | エラーがないことを検証 |
EqualError
エラーの内容を比較することができます。
nil
でも比較することが可能です。
actualObj, err := SomeFunction()
assert.EqualError(t, err, expectedErrorString)
Error
エラーが同じかどうかを確認できます。
エラーを自作した時とかに使えそうですね。
actualObj, err := SomeFunction()
if assert.Error(t, err) {
assert.Equal(t, expectedError, err)
}
NoError
こちらはエラーがないとき(nil)かどうかを判断できます。
actualObj, err := SomeFunction()
if assert.NoError(t, err) {
assert.Equal(t, expectedObj, actualObj)
}
その他の検証
メソッド | 機能 |
---|---|
Empty | 値が空か検証 |
False | falseかどうか検証 |
NotNil | nil(null)でないかを検証 |
Empty
値が空かを検証できます。
空と判定されるのは,nil
,0
,""
,false
です。
a.Empty(obj)
False
値がFalseかどうかを検証できます。
Emptyではなく、Falseかどうかを確認したいという目的の時はこちらの方が良さそうです。
assert.False(t, myBool)
NotNil
nilではないかを検証できます。
Emptyと似ていますが、コンテキストによってはこちらを採用する方が良いかもしれません。
assert.NotNil(t, err)
assertを使ってみる
assertを使ってみようと思います。
実際に下のように実装してみました。
task := entity.Todo{
Task: "test",
Deadline: time,
}
mrTodo.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(&entity.Todo{Task: "test", Done: false, Deadline: time}, nil)
taskRes, err := usc.Create(ctx, &task)
assert.NoError(t, err)
assert.NotNil(t, taskRes)
assert.Equal(t, "test", taskRes.Task)
assert.False(t, false, taskRes.Done)
assert.Equal(t, time, taskRes.Deadline)
実際にテストをしてみました。
成功した場合は下のように表示されます。
❯ go test -v ./...
=== RUN TestCreate
2024/10/16 19:26:45 deadline 2024-10-11 00:00:00 +0000 UTC
--- PASS: TestCreate (0.00s)
PASS
ok github.com/maooz4426/Todolist/usecases/interactor 0.251s
失敗した場合を確認するためにしたようなコードを追加してみました。
assert.NoError(t, err)
assert.NotNil(t, taskRes)
assert.Equal(t, "a", taskRes.Task)
log.Println("failしたよ〜ん") //追加
assert.False(t, false, taskRes.Done)
assert.Equal(t, time, taskRes.Deadline)
実際にテストを実行すると下のようになります。
❯ go test -v ./...
=== RUN TestCreate
todo_test.go:39:
Error Trace: /Users/maoz/Documents/Go/todo-go-clean/usecases/interactor/todo_test.go:39
Error: Not equal:
expected: "a"
actual : "test"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-a
+test
Test: TestCreate
2024/10/16 19:34:27 failしたよ〜ん
--- FAIL: TestCreate (0.00s)
FAIL
失敗しても実行が継続されています。
requireを使ってみる
requireは上のコードを使って、パッケージだけ変えて使ってみようと思います。
require.NoError(t, err)
require.NotNil(t, taskRes)
require.Equal(t, "a", taskRes.Task)
log.Println("failしたよ〜ん")
require.False(t, false, taskRes.Done)
require.Equal(t, time, taskRes.Deadline)
requireではassertと違い、テストを失敗すると、そこで実行が終了します。
❯ go test -v ./...
=== RUN TestCreate
todo_test.go:39:
Error Trace: /Users/maoz/Documents/Go/todo-go-clean/usecases/interactor/todo_test.go:39
Error: Not equal:
expected: "a"
actual : "test"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-a
+test
Test: TestCreate
--- FAIL: TestCreate (0.00s)
まとめ
tesitifyパッケージを使うと楽にテストを書くことができるのに加え、多くのメソッドがあるためコードを読みやすく書くこともできると感じました。
ぜひテストケースを書くときは使ってみつつ、やりたいことを抽象化したメソッドがないか調べてみてください
Go関連の記事