Pointers 使用指针接收器方法创建包装现有类型的接口
我需要测试一个使用谷歌云Pubsub的应用程序,因此必须包装其类型Pointers 使用指针接收器方法创建包装现有类型的接口,pointers,go,interface,Pointers,Go,Interface,我需要测试一个使用谷歌云Pubsub的应用程序,因此必须包装其类型Pubsub.Client和Pubsub.Subscriber,以便进行测试。然而,尽管进行了多次尝试,我还是无法获得一个可编译的接口 我试图包装的方法的定义如下: func (s *Subscription) Receive( ctx context.Context, f func(context.Context, *Message)) error func (c *Client) Subscription(id st
Pubsub.Client
和Pubsub.Subscriber
,以便进行测试。然而,尽管进行了多次尝试,我还是无法获得一个可编译的接口
我试图包装的方法的定义如下:
func (s *Subscription) Receive(
ctx context.Context, f func(context.Context, *Message)) error
func (c *Client) Subscription(id string) *Subscription
这是当前的代码。Receiver
接口(包装Subscriber
)似乎可以工作,但我怀疑它可能需要更改才能修复SubscriptionMaker
,因此我将两者都包括在内
注意:我已经尝试了几种引用和取消引用指针的变体,所以请不要告诉我更改,除非您解释了为什么建议的配置是正确的,或者您亲自验证了它是否编译
导入(
“上下文”
“cloud.google.com/go/pubsub”
)
类型接收机接口{
接收(context.context,func(ctx context.context,msg*pubsub.Message))(错误)
}
//Pubsub订阅实现接收器
var_u接收器=&pubsub.Subscription{}
类型SubscriptionMaker接口{
订阅(名称字符串)(s接收方)
}
//Pubsub客户端实现SubscriptionMaker
var_uusubscriptionMaker=pubsub.Client{}
当前错误消息:
common_types.go:21:5: cannot use "cloud.google.com/go/pubsub".Client literal (type "cloud.google.com/go/pubsub".Client) as type SubscriptionMaker in assignment:
"cloud.google.com/go/pubsub".Client does not implement SubscriptionMaker (wrong type for Subscription method)
have Subscription(string) *"cloud.google.com/go/pubsub".Subscription
want Subscription(string) Receiver
这是办不到的
在接口上定义方法的类型签名时,它必须完全匹配
func(c*客户端)订阅(id字符串)*订阅
返回一个*订阅
,*订阅
是一个有效的接收者
,但它不被视为符合接口方法订阅(字符串)接收者
。Go需要精确匹配函数签名,而不是它通常用于接口的duck类型。首先,对于大多数使用,使用包可能是测试pubsub更容易的方法。当然,您的特定问题可以应用于任何库,下面的方法对许多事情都很有用,而不仅仅是模仿pubsub
您更广泛的目标是使用接口来模拟这样的库,这是可行的。但是,当您希望模拟的库返回无法模拟的具体类型时(可能是由于未报告的字段),情况就复杂了。所采用的方法比通常值得采用的方法复杂得多,因为可能有更简单的方法来测试代码 但是如果您打算这样做,那么您必须采取的方法是不要将整个包包装在接口中,而不仅仅是您希望模拟的特定方法中 您将需要包装您希望模拟的任何类型,这些类型也是由您的接口返回或接受的。这通常意味着您还需要修改生产代码(不仅仅是测试代码),因此这有时可能会破坏现有代码库的交易 我以前通常在模拟标准库的sql驱动程序时这样做,但这里也可以应用相同的方法。本质上,您需要为
pubsub
库创建一个包装包,甚至在生产代码中也要使用它。同样,这可能会对现有的代码库造成相当大的干扰,但为了说明起见。使用定义的接口:
package mypubsub
import "cloud.google.com/go/pubsub"
type Receiver interface {
Recieve(context.Context, func(context.Context, *pubsub.Message) error)
}
type SubscriptionMaker interface {
Subscription(string) Receiver
}
然后,您可以包装默认实现,以便在生产代码中使用:
// defaultClient wraps the default pubsub Client functionality.
type defaultClient struct {
*pubsub.Client
}
func (d defaultImplementation) Subscription(name string) Receiver {
return d.Client.Subscription()
}
当然,您需要扩展这个包来包装您正在使用的大部分或全部pubsub
包。这可能有点令人畏惧
但是一旦你完成了,那么在你的代码中的任何地方使用你的
mypubsub
包,而不是直接依赖pubsub
包。现在,您可以轻松地在任何需要测试的地方交换模拟实现。定义了Subscription
函数以返回*Subscription
,但在您的界面中它返回Receiver
。类型必须相同。*Subscription
实现Receiver
接口并不重要。类型必须完全匹配。因此不可能做到这一点,那么?如果您在界面中定义Subscription
以返回*pubiub.Subscription
,它将起作用。这无法实现使其在测试中可模拟的目标。所以这是不可能的。您可以实现这一点,但它需要使用自定义定义的类型来包装所有返回的类型。因此,您必须创建一个新类型来包装*pubsub.Subscription
,并在整个应用程序中使用它,甚至为您的非测试代码创建一个自定义包装器。是的,这是一个很大的痛苦。这通常是不值得的。