こんにちは、@tkitsunaiです
Go 1.13beta1がリリースされていたので、errors周りについて色々挙動をテストしてみました
Go 1.13beta1のダウンロードはここからできます -> Downloads - The Go Programming Language
リリースノートはこちら -> Go 1.13 Release Notes - The Go Programming Language
この記事のコードを直接見る場合はGitHubへどうぞ
github.com
テストのAssertionにgithub.com/stretchr/testify
を利用しています
基本:新しくエラーを作る
err := errors.New("it is error")
assert.EqualError(t, err, "it is error")
結果
=== RUN Testエラーを新しく作成する
--- PASS: Testエラーを新しく作成する (0.00s)
エラーをWrapする
err1 := errors.New("this is error 1")
wrapError := fmt.Errorf("wrap error: %w", err1)
assert.EqualError(t, wrapError, "wrap error: this is error 1")
assert.Equal(t, false, wrapError == err1)
結果
=== RUN Testエラーをエラーで包み込む
--- PASS: Testエラーをエラーで包み込む (0.00s)
注意しなければならないのは、: %w
でWrapするという点です
エラーの等値性をチェックをする
新機能のerrors.Is関数を使います。
err1 := errors.New("this is error 1")
assert.True(t, errors.Is(err1, err1))
assert.True(t, err1 == err1)
結果
=== RUN Testエラーの値を同値かチェックする_生成された同じエラーを使う
--- PASS: Testエラーの値を同値かチェックする_生成された同じエラーを使う (0.00s)
同じインスタンスを使っているので、等値。Isを使わない場合の動作も従来通り等値。
err1 := errors.New("this is error 1")
err2 := errors.New("this is error 1")
assert.Equal(t, false, errors.Is(err1, err2))
結果
=== RUN Testエラーの値を同値かチェックする_エラーのテキストは同じ
--- PASS: Testエラーの値を同値かチェックする_エラーのテキストは同じ (0.00s)
もちろんインスタンスが違うので、等値ではない
Wrapされたエラーに対してIsでチェックする
今回の目玉
err := errors.New("this is error")
wrappedError := fmt.Errorf("wrap error: %w", err)
assert.True(t, errors.Is(wrappedError, err))
結果
=== RUN Test包まれた最初のエラーであることを同じかを判定する
--- PASS: Test包まれた最初のエラーであることを同じかを判定する (0.00s)
Isを使うことで、WrapされたwrappedErrorがerrであることが確認できました
良いですね。
では何回もWrapするとどうでしょうか
err := errors.New("this is error")
wrappedError1 := fmt.Errorf("one wrap: %w", err)
wrappedError2 := fmt.Errorf("two wrap: %w", wrappedError1)
assert.True(t, errors.Is(wrappedError2, err))
結果
=== RUN Test包まれた最初のエラーであること同じかを判定する_何回も包む
--- PASS: Test包まれた最初のエラーであること同じかを判定する_何回も包む (0.00s)
これも、Isを使うと複数回包まれたwrappedError2がerrであることが確認できました
正しくWrapできなかった場合
Wrapするには、%w
でフォーマットしてあげる必要がありました
%w
以外でWrapしたつもりになったらどうでしょうか
err := errors.New("error")
wrappedError := fmt.Errorf("wrap error: %s", err)
assert.Equal(t, false, errors.Is(wrappedError, err))
例えば%sで包みます
結果
=== RUN Testエラーを包む時にフォーマットしない場合は同一性が担保できない
--- PASS: Testエラーを包む時にフォーマットしない場合は同一性が担保できない (0.00s)
等値ではないことが確認できました
errors.Asを使って、エラー型を変換する
As関数も新機能です
独自エラー型を使う場合やエラーそのものを取り扱いたい場合にはAsが使えそうです
type NotFoundError struct {
Err string
Cause string
}
func (n NotFoundError) Error() string {
return fmt.Sprintf("%s: %v", n.Err, n.Cause)
}
func FindHoge() error {
return &NotFoundError{
Err: "not found error",
Cause: "i dont know",
}
}
func Testエラーに対しAsを利用して指定したエラー型に変換する(t *testing.T) {
if err := FindHoge(); err != nil {
var notFoundErr *NotFoundError
if errors.As(err, ¬FoundErr) {
assert.Equal(t, "i dont know", notFoundErr.Cause)
return
}
}
t.Fail()
}
FindHoge()を実行した結果、返ってきたerrorインターフェースがNotFoundErrorであるか。
もしNotFoundErrorに変換できていれば、bool値でtrueが返ってくる。
結果
=== RUN Testエラーに対しAsを利用して指定したエラー型に変換する
--- PASS: Testエラーに対しAsを利用して指定したエラー型に変換する (0.00s)
期待通りです
では、変換できない場合
type UnknownError struct {
Err string
}
func (s UnknownError) Error() string {
return s.Err
}
func FindSomething() error {
return &UnknownError{
Err: "unknown error",
}
}
func Testエラーに対しAsを利用して指定したエラー型に変換したけど変換できなかった(t *testing.T) {
err := FindSomething()
var notFoundErr *NotFoundError
assert.Equal(t, false, errors.As(err, ¬FoundErr))
assert.Nil(t, notFoundErr)
}
FindHogeで返ってくるのはUnknownError型のため、NotFoundErrorに変換できません
結果
=== RUN Testエラーに対しAsを利用して指定したエラー型に変換したけど変換できなかった
--- PASS: Testエラーに対しAsを利用して指定したエラー型に変換したけど変換できなかった (0.00s)
これも期待通りです。notFoundErrをポインタにしておかないと、zero value structとなるので注意が必要です
WrapされたエラーをAsで変換する
func TestWrapされたエラーに対しAsを利用して指定したエラー型に変換する(t *testing.T) {
err := FindHoge()
wrapError := fmt.Errorf("wrapping error: %w", err)
var notFoundErr *NotFoundError
if errors.As(wrapError, ¬FoundErr) {
assert.NotNil(t, notFoundErr)
assert.EqualError(t, notFoundErr, "not found error: i dont know")
return
}
t.Fail()
}
Wrapした場合、Asでちゃんと変換できるか?
結果
=== RUN TestWrapされたエラーに対しAsを利用して指定したエラー型に変換する
--- PASS: TestWrapされたエラーに対しAsを利用して指定したエラー型に変換する (0.00s)
大丈夫ですね。
まとめ
全体的に問題なく使えるのではないでしょうか。正式リリースが楽しみです。
正式リリースはまだ先なのでそれまではxerrorsで代用しましょう。