Моя проблема не очень сложная. Сначала я напишу простой пример, а затем покажу реальный код на случай, если пропущу какие-либо тонкости.
Итак, у меня есть сторонняя библиотека:
// 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
}
}