proto.Unmarshal测试不一致地失败了。

huangapple go评论79阅读模式
英文:

proto.Unmarshal test fails inconsistently

问题

我有一些依赖Proto标准进行数据编组/解组的Redis代码。我使用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: 无法解析无效的线格式数据"))

然而,最后的断言失败了,因为期望的错误字符串应该是proto: 无法解析无效的线格式数据。显然,解决方案是进行更改,当我这样做时,错误消失了。直到我修改测试并重新运行它,此时测试再次失败,告诉我字符串应该是proto: 无法解析无效的线格式数据。这个循环无限继续下去。那么,我在这里做错了什么,如何修复这个问题?

英文:

I had some Redis code that relied on the Proto standard for marshalling/unmarshalling of data. I wrote the following test using gomega to test around 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"))

However, the final assertion fails because the expected error string was proto: cannot parse invalid wire-format data. The obvious solution here is to change it, and when I do, the error goes away. Until I modify the test and rerun it, in which case the test again fails telling me that the string should've been proto:\u00a0cannot parse invalid wire-format data. This cycle continues infinitely. So, what am I doing wrong here and how do I fix this?

答案1

得分: 2

这个问题并不像你想的那样简单;protobuf包在输出错误时会随机选择一个空格或\u00a0(我认为这是基于二进制的哈希值)。你可以在这里看到这个问题的代码片段:

// Deliberately introduce instability into the error message string to
// discourage users from performing error string comparisons.
if detrand.Bool() {
   return "proto: " // 使用非断行空格(U+00a0)
} else {
   return "proto: " // 使用普通空格(U+0020)
}

所以你遇到的问题是故意的,旨在防止用户进行你正在尝试的操作(依赖错误保持不变)。你之所以只在更改测试时看到这个问题(我猜想,并不是每次更改都会出现),是因为Go会缓存测试结果(默认情况下,只有在有更改时才运行测试)。

关于你可以对此做些什么,我首先建议你考虑一下这个测试是否真的必要。包的作者明确表示错误是不稳定的,因此以这种方式进行比较可能会在发布新版本的google.golang.org/protobuf/proto时出现问题。

protobuf包的测试通过调用detrand.Disable()来解决这个问题(例如这里)。你无法这样做,因为google.golang.org/protobuf/internal/detrand位于internal包中,因此无法访问

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

英文:

This issue is not quite as you think; the protobuf package randomly selects a space or \u00a0 when outputting an error (I believe it's based upon a hash of the binary). You can see this 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)
}

So the issue you are encountering is deliberate and aims to prevent users from doing what you are attempting (relying upon errors remaining the same). The reason that you only see this when you change a test (and, I would guess, not every time you change it) is that Go will cache the test results (by default tests are only run when something has changed).

In terms of what you can do about this I'd first suggest considering if this test is really necessary. The package authors are specifically signalling that the errors are not stable so comparing in this way may break when a new version of google.golang.org/protobuf/proto is released.

The protobuf package tests work around this by calling detrand.Disable() (e.g. here). You cannot do this because google.golang.org/protobuf/internal/detrand is under internal and, as such, not accessible.

If you really want to work around this the simplest approach is probably strings.Contains.

huangapple
  • 本文由 发表于 2022年5月24日 16:16:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/72359452.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定