在golang

时间:2016-05-27 13:37:03

标签: unit-testing go interface

我有一个包含很多子方法的接口。

type InterfaceCheckout interface {
    GetID()    int
    GetItems() []InterfaceCartItem
    // ... then like 30more methods
}

我有一个只使用GetItems方法的方法。

func GetRates(checkoutI InterfaceCheckout) []Rate {
    for _, item := range checkoutI.GetItesm() {
        p := item.GetProduct()
    }
}

我希望能够在不模仿GetRates中的所有方法的情况下测试此InterfaceCheckout方法。

我以为我能够:

  1. 创建一些较小的接口,仅指定我正在使用的方法
  2. 将输入转换为此新界面
  3. 传递给新的内部方法

    func GetRates(checkoutI InterfaceCheckout) []Rate {
        getRates(checkoutWrapper(checkoutI))
    }
    
    func getRates(checkoutI checkoutWrapper) []Rate {
        for _, item := range checkoutI.GetItesm() {
            p := item.GetProduct()
        }
    }
    
    // smaller wrapper interfaces
    type checkoutWrapper interface {
        GetItems() []InterfaceCartItem
    }
    
  4. 我遇到的问题是InterfaceCartItem返回的GetItems在界面中列出了约30种方法,而我只使用其中一种GetProduct。所以我想我可以使用相同的解决方案并使用我需要的一个方法创建一个接口,但是当我尝试更改从checkoutWrapper@GetItems()返回的类型时,golang说checkoutI不再满足{ {1}}接口,因为它从checkoutWrapper返回不同的类型,这在技术上是正确的......

    我尝试的代码不起作用

    GetItems

    接口方法验证只做了一层深度吗?

1 个答案:

答案 0 :(得分:6)

将您的界面嵌入虚假对象中。 例如:

type InterfaceCheckout interface {
    GetID() int
    GetItems() []InterfaceCartItem
}

type InterfaceCartItem interface {
    GetProduct() string
    GetID() int
}

type fakeCheckout struct {
    InterfaceCheckout
}

func (fakeCheckout) GetItems() []InterfaceCartItem {
    return []InterfaceCartItem{fakeItem{}}
}

type fakeItem struct {
    InterfaceCartItem
}

func (fakeItem) GetProduct() string {
    return "This is the end"
}

func getRates(checkoutI InterfaceCheckout) {
    for _, item := range checkoutI.GetItems() {
        fmt.Printf("%v\n", item.GetProduct())
    }
}

func main() {
    fc := fakeCheckout{}
    getRates(fc)
}

请参阅:https://play.golang.org/p/uSFegnZq7S

附注:避免使用30种方法的界面,它们会很快变得非常麻烦。

修改

  

这有效,但可以解释为什么在结构中嵌入接口

在结构中嵌入接口有点微妙。 The spec提到嵌入接口会引入其方法集,因此当您调用它总是编译的方法时。它还引入了接口类型的nil成员。

typ := reflect.TypeOf(fc)
fmt.Printf("+%v\n", typ.Field(0))

你可以看到那里的成员:

{Name:InterfaceCheckout PkgPath: Type:main.InterfaceCheckout Tag: Offset:0 Index:[0] Anonymous:true}

它如何在运行时工作?

  • 当你调用一个你的类型覆盖 1 的方法时,一切都很好:你的方法被调用
  • 当你调用一个你不会覆盖的方法时,调用将进入嵌入对象,即nil。这很像下面的恐慌:

    var ic InterfaceCheckout // nil, just like your embedded type
    ic.GetItems()
    

<子> 1。类型可以自由覆盖其嵌入类型

带来的方法