goでtestをしているときにtestifyパッケージのrequire.ErrorIsでメソッドを用いて、エラーの検証がしたいときに、下のようなエラーが発生したことがありました。
return fmt.Errorf("error: %s", gorm.ErrNotFound)
require.ErrorIs(t, err, tt.wantErr)
エラーをよく見ると欲しいエラー文と実際のエラー文が同じなのに、テストが通りませんでした。
--- FAIL: TestUpdate (0.00s)
--- FAIL: TestUpdate (0.00s)
code_test.go:173:
Error: Target error should be in err chain:
expected: "error: not found"
in chain: "error: not found"
FAIL
エラー文が同一なのに通らなかったテストを通した際にわかったことをメモとしてまとめておきたいなと思います。
そもそもErrorIsメソッドとは
ErrorIsメソッドは、errors
パッケージのIsメソッドで行っているエラーチェーン内に特定のエラーが含まれているかを調べるメソッドです。
例えば、goのORMライブラリとして有名なGORMで用意されているNotFound
エラーがエラーチェーンに含まれてるかどうかという時にerrors.Is
メソッドを使用できます。
↓
require.ErrorIs
では、errors.Is
のようにテストケース内で発生したエラーのエラーチェーンにおいて特定のエラーが含まれているかどうかができる、抽象メソッドです。
どうしてエラーが出てしまったのか
エラー文は同じだけど違うエラーが出ているということは、ErrorIsメソッド
の話で気づけるようにエラーチェーンが違う可能性が考えられると思います。
ではどうしてエラーチェーンが異なってしまったかというと、Goにおけるフォーマット指定子
が関係しています。
フォーマット指定子は下のような%
で指定して、表示形式を決めているものです。
fmt.Sprintf("hello %s", w)
フォーマット指定子は%
のあとに指定子で指定されている文字によって指定できるものが変わってきます。
一部例を出すと下のようになっています。
指定子 | 型 |
---|---|
%v | value |
%t | bool |
%d | 10進数(int) |
%s | 文字列(string) |
%p | ポインタ |
今回のコードでは%s
でstringを指定していると思います。
fmt.Errorf("error: %s", repository.ErrNotFound)
こうすることによってerror: not found
で一つのエラーになってしまいます。
今回はエラーチェーンとしてerror: not found
からgorm.ErrNotFound
のエラー文であるnot found
がErrorIs
メソッドで取り出されて欲しいのに、エラーチェーンが形成されず取り出すことができなかったので、テストが通らなかったというわけです。
エラーチェーンが形成されるようにするには、下のように実装します。
fmt.Errorf("mutual fund not found: %w", repository.ErrNotFound)
ドキュメントによると、%w
を指定子に指定することでfmtで作成してくれるエラーにUnwrap
メソッドを実装してくれます。
こうすることでエラーチェーンとして機能し、ErrorIs
メソッドでエラーが含まれているかを調べるようにできるようになり、エラーが辿りやすくなります。
fmt.Errorf
でエラー文を整形したいとなった時、テストでエラーが通らなくなった時はぜひ参考にしてください。
Go関連の記事