英文:
How do I implement Python's mock ANY in Golang
问题
我想要能够比较两个字典,同时忽略某些字段的值。在Python中,使用mock.ANY
非常容易实现。
以下是Python代码示例:
from unittest.mock import ANY
actual = {'userName':'bob', 'lastModified':'2012-01-01'}
expected = {'userName':'bob', 'lastModified': ANY}
assert actual == expected
这里是我在Go语言中的尝试,利用了cmp
包,该包允许你实现一个Equal
方法(https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal),但是它不起作用。我认为这是因为我的MatchAll
结构体无法“赋值”给字符串。有没有办法解决这个问题?
以下是Go语言的代码示例:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
type MatchAll string
func (m MatchAll) Equal(v string) bool {
return true
}
func main() {
matchAll := MatchAll("bruh")
actual := map[string]interface{}{
"key": matchAll,
}
expected := map[string]interface{}{
"key": "foobar",
}
fmt.Println(cmp.Equal(expected, actual))
}
希望对你有帮助!
英文:
I want to be able to compare two dictionaries while ignoring the value of some fields. This is extremely easy in Python with mock.ANY
.
from unittest.mock import ANY
actual = {'userName':'bob', 'lastModified':'2012-01-01'}
expected = {'userName':'bob', 'lastModified': ANY}
assert actual == expected
Here's my attempt in Go that leverages cmp
which allows you to implement a Equal
method (https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal) but it doesn't work. I believe it's because my MatchAll
struct is not "assignable" to string. Is there anyway to get around this?
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
type MatchAll string
func (m MatchAll) Equal(v string) bool {
return true
}
func main() {
matchAll := MatchAll("bruh")
actual := map[string]any{
"key": matchAll,
}
expected := map[string]any{
"key": "foobar",
}
fmt.Println(cmp.Equal(expected, actual))
}
答案1
得分: 1
观察cmp
代码,我认为问题在于在比较映射元素时,用于检查Equal
方法存在性的类型是映射的元素类型(这里是any
),而不是两个值的具体类型(这里是string
或MatchAll
)。由于any
类型没有声明Equal
方法,因此会回退到内置的比较方式。
作为替代方案,你可以尝试使用cmp.FilterValues和cmp.Comparer函数来构建一个支持特殊的“等于任何值”的选项。例如,下面的代码似乎可以按预期工作:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
type matchAllType struct{}
var matchAll = matchAllType{}
var matchAllOption = cmp.FilterValues(
// 过滤函数:仅对使用此比较的值返回true。
func(x, y any) bool {
return x == matchAll || y == matchAll
},
// 比较函数:对过滤器接受的值对调用。
cmp.Comparer(func(x, y any) bool { return true }),
)
func main() {
actual := map[string]any{
"key": matchAll,
}
expected := map[string]any{
"key": "foobar",
}
fmt.Println(cmp.Equal(expected, actual, matchAllOption))
}
始终返回true的比较函数也可以替换为cmp.Ignore(),不过我不确定语义是否完全相同。至少在上面的示例中它是有效的。
还有一个选项是cmpopts.IgnoreMapEntries,可以按照以下方式使用,不过语义略有不同:
var matchAllOption = cmpopts.IgnoreMapEntries(func(_, v any) bool { return v == matchAll })
不同之处在于,使用此选项时,即使actual
字典不包含与匹配键对应的条目,比较结果也会为true
,因为内置的映射比较会测试“递归调用所有非被忽略的映射条目的Equal方法是否相等”,强调部分已添加。相比之下,之前的解决方案仍要求另一个映射包含具有相同键的某个值。
英文:
Looking at the cmp
code, I believe the problem here is that when comparing map elements, the type that's used to check for the existence of the Equal
method is the element type of the map (here, any
), not the concrete type of one of the two values (here, string
or MatchAll
). Since the any
type does not declare an Equal
method, it falls back to the built-in comparisons.
As an alternative, you could potentially use the cmp.FilterValues and cmp.Comparer functions to construct an option that supports your special "equal-to-anything" value. For example, this would appear to work as intended:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
type matchAllType struct{}
var matchAll = matchAllType{}
var matchAllOption = cmp.FilterValues(
// Filter function: returns `true` only for values using this comparison.
func(x, y any) bool {
return x == matchAll || y == matchAll
},
// Comparison function: called for pairs of values the filter accepts.
cmp.Comparer(func(x, y any) bool { return true }),
)
func main() {
actual := map[string]any{
"key": matchAll,
}
expected := map[string]any{
"key": "foobar",
}
fmt.Println(cmp.Equal(expected, actual, matchAllOption))
}
The always-true comparison function could possibly be replaced with cmp.Ignore() as well, though I'm not entirely sure whether the semantics are exactly the same. It does work in the above example, at least.
There is also the cmpopts.IgnoreMapEntries option, which could be used as follows, but with slightly different semantics:
var matchAllOption = cmpopts.IgnoreMapEntries(func(_, v any) bool { return v == matchAll })
The difference is that with this option, the comparison would be true
even if the actual
dictionary did not contain an entry with a matching key at all, because the built-in map comparison tests whether "recursively calling Equal on all non-ignored map entries report equal", emphasis added. By contrast, the earlier solutions still require the other map to contain some value with the same key.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论