![](/img/trans.png)
[英]Necessity of calling Elem() method on pointer-receiver for struct reflection
[英]Create interface wrapping existing types with pointer-receiver methods
我需要测试一个使用 Google Cloud Pubsub 的应用程序,因此必须包装其类型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
,所以我已经包含了两者。
注意:我已经尝试了几种引用和取消引用指针的变体,所以请不要告诉我改变它,除非你解释了为什么你的建议配置是正确的,或者你已经亲自验证了它编译。
import (
"context"
"cloud.google.com/go/pubsub"
)
type Receiver interface {
Receive(context.Context, func(ctx context.Context, msg *pubsub.Message)) (err error)
}
// Pubsub subscriptions implement Receiver
var _ Receiver = &pubsub.Subscription{}
type SubscriptionMaker interface {
Subscription(name string) (s Receiver)
}
// Pubsub clients implement SubscriptionMaker
var _ SubscriptionMaker = 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
首先,对于大多数用途,使用ptest
包可能是一种更容易测试 pubsub 的方法。 但是,当然,您的具体问题可以适用于任何库,下面的方法对很多事情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
包。 现在,您可以在任何需要测试的地方轻松更换模拟实现。
做不到。
在接口上定义方法的类型签名时,它必须完全匹配。 func (c *Client) Subscription(id string) *Subscription
返回一个*Subscription
,并且一个*Subscription
是一个有效的Receiver
,但它不符合接口方法Subscription(string) Receiver
。 Go 需要对函数签名进行精确匹配,而不是它通常用于接口的鸭子类型样式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.