What's the idiomatic way to compare fields based only on interfaces disregarding underlying types (there isn't a "cmpopts.UseInterfaceOnly()")?

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

What's the idiomatic way to compare fields based only on interfaces disregarding underlying types (there isn't a "cmpopts.UseInterfaceOnly()")?

问题

我有一堆遵循同一个接口的数据结构,并且我想编写一个单元测试,可以适用于所有实现。

在这种情况下,如果我使用相同的结构类型,我希望使用cmp.Diff,但我找不到一种告诉cmp根据接口表面进行比较的方法。

以下是一个简单的示例:

type Person interface {
	Name() string
	Age() int
}

type yearlyPerson struct {
	name     string
	yearsOld int
}

type dailyPerson struct {
	name    string
	daysOld int
}

func (dp *dailyPerson) Age() int {
	return dp.daysOld / 365
}

// 其他3个函数是简单的字段返回函数。

假设有许多这样的字段,在单元测试中,我可能希望使用cmp.Diff来捕获和显示它发现的任何差异,但据我了解,cmp只关注具体类型。

是否有一种惯用的方法来进行基于接口字段的比较?我没有仔细查找,但我没有找到一个专门用于接口表面的“cmp/cmpopts”库。

我能想到的最好的方法是编写一个辅助库,手动构建一个具有与接口表面匹配的导出字段的具体类型,然后相应地使用cmp.Diff,就像我在这里做的一样:https://go.dev/play/p/TtEomRGXTtQ

这是最好的方法,还是有更好的方法?

英文:

I have a bunch of data structs that all adhere to an interface, and I'd like to write a unit test that can work on all implementations.

This is a case where, if I'd use the same struct type, I'd want to use cmp.Diff, but I can't find a way to tell cmp to compare based on interface surface.

Toy example:

type Person interface {
	Name() string
	Age() int
}

type yearlyPerson struct {
	name     string
	yearsOld int
}

type dailyPerson struct {
	name    string
	daysOld int
}

func (dp *dailyPerson) Age() int {
	return dp.daysOld / 365
}

// Other 3 functions are trivial field returns.

If we pretend there are many such fields, in a unit test, I might want to use cmp.Diff to nicely capture and display any discrepancies it picks up, but IIUC, cmp only ever focuses on concrete types.

Is there an idiomatic way to do interface-field-based comparisons? I didn't look too hard, but I haven't found a "cmp/cmpopts but for interface surfaces specifically" library.

Off the top of my head, the best I can think of is a helper library that manually constructs a concrete type with exported fields matching the interface surface, and then using cmp.Diff accordingly, like I do here: https://go.dev/play/p/TtEomRGXTtQ

Is this the best way, or is there something much better?

答案1

得分: 0

cmp.Diff是专门用于比较数据的。一个接口没有数据,只有方法。只有具体类型才有字段。

基于接口字段的比较

不存在;接口没有字段。

构建一个具有与接口表面匹配的导出字段的具体类型

不存在;接口表面只有方法,没有指定字段。你可以手动完成这个操作,因为你知道哪些方法只是底层字段的获取器/设置器,但运行时工具无法做出这样的假设。大多数接口不指定获取器/设置器,因为这通常不是它们的目的;它们用于指定行为。

因此,基本上,你所描述的只能通过为每个接口类型编写自己的比较函数来实现,就像你在示例中所做的那样。

英文:

cmp.Diff is specifically looking at data. An interface, ignoring the underlying type, has no data. It only has methods. Only concrete types have fields.

> interface-field-based comparison

does not exist; interfaces do not have fields.

> constructs a concrete type with exported fields matching the interface surface

does not exist; the interface surface is only methods and specifies no fields. You can do this manually because you know which methods are just getters/setters for underlying fields, but runtime tooling can't make that assumption. Most interfaces do not specify getters/setters, as that's not generally what they're for; they're for specifying behavior.

So, essentially, what you're describing can only realistically be achieved by writing the comparison yourself for each interface type, as you've done in your example.

huangapple
  • 本文由 发表于 2023年4月22日 00:12:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76074960.html
匿名

发表评论

匿名网友

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

确定