LoginSignup
23
11

More than 5 years have passed since last update.

テスト等でのreflect.DeepEqualの問題点とその解決策

Last updated at Posted at 2018-03-13

等価性

Go言語では==は等値でなく等価ですが、もちろんpointerの参照先まで見てくれるわけではないので、
sliceやmap、pointerまたはそれらを含む構造体などの等価性を比較したい場合
通常、reflect.DeepEqualを使用するものだと思います。

ただ、テスト用途としてはいくつか問題があります。

reflect.DeepEqual

まずreflect.DeepEqualはunexportの値まで参照します。
基本的に内部表現にまで影響を受けるべきではありませんし
unexportの値を取得するためにunsafeな部分を扱う必要もあるので気持ち悪いです。

また、内部表現が異なっていても等価を表現したいこともあります。
良い例がGo言語1.9で追加されたMonotonic clockの影響を受けたTime型です。
うるう秒の巻き戻しに対応するために導入された経過時間を保持するとかいう生意気なあれです。
2つあるTime型変数がnano秒にいたるまで両方ともぴったり同時刻だったとしてもMonotonicのなんたらがうんたらしていると
DeepEqualの結果はfalseとなり、我々がちんたらしてしまうわけです。おそろしいですね・・・

もちろんTime型だけを比較したいだけであればEqual関数が用意されているのでそれを使えばいいわけですが
Time型が入った構造体などをDeepEqualすると問題になることは極めて稀ですが少しややこしくなってしまいます。

Equal(T)bool関数があればそれを使っていただきたいものですね。
Time型以外にもnet.IPやregexp/syntax.RegexpもEqual関数を持ってますしね。

go-cmp

というわけで強力安全シンプルな比較ライブラリgo-cmpです。
https : //github.com/google/go-cmp
Equal関数が存在すればそれを考慮してくれますし、
カスタム等価関数を使用してfloatで誤差を許容することもできます。
unexportを扱うか否かも決められます。
何が違うかの文字列も出力してくれます。便利ですね。
しかもFunctional Option Patternも使ってます、おしゃれです。

自分は結局使いませんでしたが、
レポジトリはgoogleでも公式ではありませんが、
まだバグもTODOもあるようですが、
image.RectangleとかEqualじゃなくてEqだったりする標準ライブラリ構造体もありますが、
みなさん。

go-cmp、使いましょう。

23
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
11