在void函数中使用的结构上模拟复杂方法

时间:2018-08-25 00:04:24

标签: go mocking tdd

我的问题并不复杂。首先,我将写一个简单的示例,然后在漏掉任何细微之处后再显示真实的代码。

所以我有一个第三方库:

// Third party library
type ThirdPartyFoo struct {}    
func (t *ThirdPartyFoo) DoFoo () *ThirdPartyFoo {
    println("I'm a third party library!")
    return t
}

我想使用该库来调用DoFoo()

type MyThing struct {
    TheFoo ThirdPartyFoo
}

func (myThing *MyThing) WhichLib () {
    myThing.TheFoo.DoFoo()
}

我想用ThirdPartyFoo的模拟版本进行测试,因为,我还应该如何验证DoFoo()是A)调用的,B)使用正确的参数调用的,以及C) MyThing根据不同的返回值正确响应吗?

我尝试使用MyThing内部的接口,而不是直接依赖ThirdPartyFoo ...

type IThirdPartyFoo interface {
    DoFoo() *IThirdPartyFoo
}

type MyThingWithI struct {
    TheFoo IThirdPartyFoo
}

func (myThing *MyThingWithI) WhichLib () {
    myThing.TheFoo.DoFoo()
}

然后调用该功能...

func main() {
    realFoo := ThirdPartyFoo{}
    realThing := MyThingWithI{
        TheFoo: &realFoo,
    }
    realThing.WhichLib()
}

但是在运行时失败:

proxyutils/serviceregistrar.go:44:9: cannot use &realFoo (type *ThirdPartyFoo) as type IThirdPartyFoo in field value:
        *ThirdPartyFoo does not implement IThirdPartyFoo (wrong type for DoFoo method)
                have DoFoo() *ThirdPartyFoo
                want DoFoo() IThirdPartyFoo

所以...我花了几个小时试图弄清楚这个问题,又花了一个小时写这篇文章...我仍然迷路了。我唯一能想到的解决方案是完全包装第三方库,如下所示:

type IThirdPartyFooWrapper interface {
    DoFoo() IThirdPartyFooWrapper
}

type ThirdPartyFooWrapper struct {
    IThirdPartyFooWrapper

    ThirdPartyFoo ThirdPartyFoo
}

func (w *ThirdPartyFooWrapper) DoFoo () IThirdPartyFooWrapper {
    w.ThirdPartyFoo.DoFoo()
    return w
}

type MyThingWithI struct {
    TheFooWrapper IThirdPartyFooWrapper
}

func (myThing *MyThingWithI) WhichLib() {
    myThing.TheFooWrapper.DoFoo()
}

但是已经有很多间接级别了,我真的很讨厌添加这个级别。这就是嘲笑的目的,但是Go语言似乎已经完全解决了所有不幸的糟糕问题,以至于开始使用不使用接口的第三方库。

最后,这是我的真实代码。 *mux.Router作为ServiceRegistrar的一部分是有问题的。

package proxyutils

import (
    "errors"
    "github.com/gorilla/mux"
    "net/http"
)

type IProxyRegistrar interface {
    Register(input Registration) error
}

type ServiceRegistrarFactory struct{}

func (_ *ServiceRegistrarFactory) Build(proxyRegistrar IProxyRegistrar, router *mux.Router) (*ServiceRegistrar, error) {
    if nil == proxyRegistrar {
        return nil, errors.New("cannot create ServiceRegistrar with nil IProxyRegistrar")
    }
    if nil == router {
        return nil, errors.New("cannot create ServiceRegistrar with nil mux.Router")
    }
    return &ServiceRegistrar{
        proxyRegistrar: proxyRegistrar,
        router:         router,
    }, nil
}

type ServiceRegistrar struct {
    proxyRegistrar IProxyRegistrar
    router         *mux.Router
}

func (r *ServiceRegistrar) Register(userRegistration Registration, f func(http.ResponseWriter, *http.Request)) error {
    err := r.proxyRegistrar.Register(userRegistration)
    if nil == err {
        var route *mux.Route
        if userRegistration.PathIsPrefix {
            route = r.router.PathPrefix(userRegistration.Path)
        } else {
            route = r.router.Path(userRegistration.Path)
        }
        route.Methods(userRegistration.Methods...).HandlerFunc(f)
        return nil
    } else {
        return err
    }
}

0 个答案:

没有答案