proto.Unmarshal 测试不一致地失败

proto.Unmarshal test fails inconsistently

我有一些 Redis 代码依赖于 marshalling/unmarshalling 数据的 Proto 标准。我使用 gomega 编写了以下测试来测试 proto.Unmarshal:

b64Decoded, err := base64.StdEncoding.DecodeString("derp")
Expect(err).ShouldNot(HaveOccurred())

var item testRecord
err = proto.Unmarshal(b64Decoded, &item)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).Should(Equal("proto:\u00a0cannot parse invalid wire-format data"))

然而,最终的断言失败了,因为预期的错误字符串是 proto: cannot parse invalid wire-format data。这里显而易见的解决方案是更改它,当我这样做时,错误就会消失。直到我修改测试并重新运行它,在这种情况下,测试再次失败,告诉我字符串应该是 proto:\u00a0cannot parse invalid wire-format data。这个循环无限继续下去。那么,我在这里做错了什么以及如何解决这个问题?

这个问题跟你想的不太一样; protobuf 包在输出错误时随机选择 space 或 \u00a0 (我相信它基于二进制的散列)。你可以看到这个 here:

// Deliberately introduce instability into the error message string to
// discourage users from performing error string comparisons.
if detrand.Bool() {
   return "proto: " // use non-breaking spaces (U+00a0)
} else {
   return "proto: " // use regular spaces (U+0020)
}

所以您遇到的问题是故意的,旨在阻止用户执行您正在尝试的操作(依赖于保持不变的错误)。你只在更改测试时看到它的原因(我猜,不是每次更改它时)是 Go 将缓存测试结果(默认情况下,测试仅 运行 当有些东西变了)。

关于你能做些什么,我首先建议考虑这个测试是否真的有必要。软件包作者特别指出错误不稳定,因此在发布新版本 google.golang.org/protobuf/proto 时,以这种方式进行比较可能会中断。

protobuf 包测试通过调用 detrand.Disable() 解决这个问题(例如 here). You cannot do this because google.golang.org/protobuf/internal/detrand is under internal and, as such, not accessible.

如果您真的想解决这个问题,最简单的方法可能是 strings.Contains