go-cmpでmap内の時間文字列を近似比較する

以下のような関数をテストする際、期待する値もtime.Now()で生成して概ね問題ない。

func f() map[string]interface{} {
    return map[string]interface{}{
        "time": time.Now().Format("2006-01-02 15:04:05"),
    }
}

ただし関数内に多くの処理がある場合など、ごく稀に時間がズレてFAILしてしまうことがある。
こういった関数を作らないことで回避することもできるが、cmp.FilterPathを使うと次のような比較処理を実装できる。

  1. mapの特定のキーに対して、
  2. 値をinterface{}からtime.Timeに変換し、
  3. 2つの値の差が1秒以下ならOKとする
cmp.FilterPath(func(p cmp.Path) bool {
    if mi, ok := p.Index(-1).(cmp.MapIndex); ok {
        // 1. mapのキーがtimeの場合はComparerを使う
        return mi.Key().String() == "time"
    }
    return false
}, cmp.Comparer(func(x, y interface{}) bool {
    // 2. time.Timeに変換する
    xt, ok := parseTime(x)
    if !ok {
        return false
    }
    yt, ok := parseTime(y)
    if !ok {
        return false
    }

    if xt.Before(yt) {
        // xt.Sub(yt)が負の値にならないように入れ替える
        xt, yt = yt, xt
    }

    // 3. Durationが1秒以下ならOKとする
    return xt.Sub(yt) <= time.Second
}))

使用例はこちらgo.dev