doushang8846 2015-11-13 00:34
浏览 12
已采纳

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

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.

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. In my test, I would call NewAwesome() passing in a testFetcher, a struct which also implements the interface.

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. I believe this is disallowed because to would lead to something like slicing (in the polymorphic sense, not the golang sense). 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.

  • 写回答

1条回答 默认 最新

  • doubu1950 2015-11-13 02:38
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器