简体   繁体   English

当一个具体方法的签名指向另一个具体类型而不是其接口时,我该如何模拟多个类型?

[英]How can I mock multiple types when the signature of a concrete method refers to another concrete type, not its interface?

I'm making use of a third party library that doesn't have any interfaces for its classes. 我正在使用一个第三方库,该库的类没有任何接口。 I can use them in my structs no problem, but they have side effects that I want to avoid when unit testing. 我可以在结构中使用它们,没问题,但是它们具有副作用,在单元测试中要避免。

// Somewhere there are a couple structs, with no interfaces. I don't own the code.
// Each has only one method.
type ThirdPartyEntry struct {}
func (e ThirdPartyEntry) Resolve() string {
    // Do some complex stuff with side effects
    return "I'm me!"
}

// This struct returns an instance of the other one.
type ThirdPartyFetcher struct {}
func (f ThirdPartyFetcher) FetchEntry() ThirdPartyEntry {
    // Do some complex stuff with side effects and return an entry
    return ThirdPartyEntry{}
}

// Now my code.
type AwesomeThing interface {
    BeAwesome() string
}
// I have a class that makes use of the third party.
type Awesome struct {
    F ThirdPartyFetcher
}
func (a Awesome) BeAwesome() string {
    return strings.Repeat(a.F.FetchEntry().Resolve(), 3)
}
func NewAwesome(fetcher ThirdPartyFetcher) Awesome {
    return Awesome{
        F: fetcher,
    }
}

func main() {
    myAwesome := NewAwesome(ThirdPartyFetcher{})
    log.Println(myAwesome.BeAwesome())
}

This works! 这可行! But I want to write some unit tests, and so I'd like to Mock both the third party structs. 但是我想编写一些单元测试,所以我想模拟两个第三方结构。 To do so I believe I need interfaces for them, but since ThirdPartyFetcher returns ThirdPartyEntrys, I cannot figure out how. 为此,我相信我需要它们的接口,但是由于ThirdPartyFetcher返回ThirdPartyEntrys,所以我不知道如何做。

I created a pair of interfaces which match up with the two third party classes. 我创建了一对与两个第三方类匹配的接口。 I'd like to then rewrite the Awesome struct and method to use the generic Fetcher interface. 然后,我想重写Awesome结构和方法以使用通用Fetcher接口。 In my test, I would call NewAwesome() passing in a testFetcher, a struct which also implements the interface. 在我的测试中,我将调用NewAwesome()并传递一个testFetcher,该结构也实现了该接口。

type Awesome struct {
    F Fetcher
}
func NewAwesome(fetcher Fetcher) Awesome {
    return Awesome{
        Fetcher: fetcher,
    }
}

type Entry interface {
    Resolve() string
}
// Double check ThirdPartyEntry implements Entry
var _ Entry = (*ThirdPartyEntry)(nil)

type Fetcher interface {
    FetchEntry() Entry
}
// Double check ThirdPartyFetcher implements Fetcher
var _ Fetcher = (*ThirdPartyFetcher)(nil)

I omit the test code because it's not relevant. 我忽略了测试代码,因为它不相关。 This fails on the last line shown. 这在显示的最后一行失败。

./main.go:49: cannot use (*ThirdPartyFetcher)(nil) (type *ThirdPartyFetcher) as type Fetcher in assignment:
*ThirdPartyFetcher does not implement Fetcher (wrong type for FetchEntry method)
    have FetchEntry() ThirdPartyEntry
    want FetchEntry() Entry

The signatures are different, even though I already showed that ThirdPartyEntry implements Entry. 即使我已经表明ThirdPartyEntry实现Entry,签名也有所不同。 I believe this is disallowed because to would lead to something like slicing (in the polymorphic sense, not the golang sense). 我认为这是不允许的,因为这样会导致切片(在多态意义上而不是在golang意义上)。 Is there any way for me to write a pair of interfaces? 有什么办法可以编写一对接口? It should be the case that the Awesome class doesn't even know ThirdParty exists - it's abstracted behind the interface and injected when main calls NewAwesome. 应该是Awesome类甚至不知道ThirdParty存在的情况-它在接口后面抽象并在main调用NewAwesome时注入。

It's not very pretty, but one way would be to: 它不是很漂亮,但是一种方法是:

type fetcherWrapper struct {
    ThirdPartyFetcher
}

func (fw fetcherWrapper) FetchEntry() Entry {
    return fw.ThirdPartyFetcher.FetchEntry()
}

I'd say mocking things that return structs vs interfaces is a relatively common problem without any great solutions apart from a lot of intermediate wrapping. 我会说嘲笑返回结构vs接口的东西是一个相对普遍的问题,除了很多中间包装之外,没有任何好的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM