如何使用相互依赖的接口方法模拟结构

分享于2023年03月08日 go go-interface mocking testing unit-testing 问答
【问题标题】:How to mock structs with interdependent interface methods如何使用相互依赖的接口方法模拟结构
【发布时间】:2023-03-05 02:46:01
【问题描述】:

我在 Go 中为一个相当常见的用例/模式编写单元测试时遇到了麻烦。

想象一下,如果你愿意的话,是这样的:

package main

type Resource struct {
    name string
}

type ResourceManager interface {
    GetResource(id string) (*Resource, error)
    GetAllResources() ([]*Resource, error)
}

type ResourceManagerImpl struct {
}

func (r *ResourceManagerImpl) GetResource(id string) (*Resource, error) {
    resource := &Resource{}
    var err error

    // fetch resource.
    // ...

    return resource,  err
}

func (r *ResourceManagerImpl) GetAllResources() ([]*Resource, error) {
    var resources []*Resource
    var err error

    // for _, id := range ids {
    //  resource = r.GetResource(id)
    //  resources = append(resources, resource)
    // }

    return resources, err
}

GetAllResources 根据需要反复调用 GetResource 是一种常见模式。

我可以使用 gomock testify 来测试 GetResource 的所有排列。但是,在测试 GetAllResource 时,我想模拟 GetResource 。否则,测试将成为一场噩梦。如果 Java 使用部分模拟,这就是在 easymock mockito 中执行此操作的方式。但是,尚不清楚如何在 Golang 中实现相同的目标。

具体来说,我找不到如何部分模拟 struct 。大多数建议都围绕着打破 struct s,但在这种情况下, struct 已经处于最低限度。为了测试,不要破坏 ResourceManager 接口(分为单接口和多接口)似乎是一个公平的要求,因为这没有多大意义,而且充其量是笨拙的,并且随着更多这样的方法进入,也不能很好地扩展界面。


【解决方案1】:

我是这样处理这种情况的:

func (r *ResourceManagerImpl) GetAllResources() ([]*Resource, error) {
   return getAllResources(r)
}


func getAllResources(r ResourceManager) ([]*Resource,error) {
  ...
}

然后你用模拟的 r 测试 getAllResources 而不是 GetAllResources 。如果您遇到从代码中调用 GetAllResources 并且必须模拟 GetAllResources 的情况,您可以这样做:

var getAllResources=func(r ResourceManager) ([]*Resource,error) {
...
}

并将 getAllResources 分配给一个测试实例。

【讨论】:

  • 看起来这是我们能做的最好的了。生成的代码仍然有多余的部分,但它可以工作。现在就可以了。