在 golang 中模拟外部结构依赖项

Posted

技术标签:

【中文标题】在 golang 中模拟外部结构依赖项【英文标题】:Mocking external struct dependencies in golang 【发布时间】:2017-02-23 17:45:22 【问题描述】:

我很难找到一种在 golang 中编写可测试代码的惯用方式。我了解接口的重要性及其在测试中的用途,但我还没有弄清楚如何模拟/测试外部结构依赖项。

作为示例,我编写了以下代码,它模拟了在 GitHub 上创建拉取请求的包装器。

type GitHubService interface 



type gitHubService struct 
    CreatePullRequest(...) (PullRequest,error)


func (s gitHubService) CreatePullRequest(...) (PullRequest,error) 
  tp := github.BasicAuthTransport
      Username: strings.TrimSpace(/*.....*/),
      Password: strings.TrimSpace(/*.....*/),
    

    client := github.NewClient(tp.Client())
  pr,err := client.Repositories.CreatePullRequest(...)

  ...


func TestPullRequest(t *testing.T) 
  service := gitHubService
  pr,err := service.CreatePullRequest(...)
  ...

如果我正在为 GitHubService.CreatePullRequest(...) 编写单元测试,我想模拟对 client.Repositories.CreatePullRequest(...) 的调用,甚至可能模拟对 github.NewClient(...) 的调用,以返回我可以控制的模拟实现。

使用诸如gomock 之类的工具,您似乎对结构和包函数不走运。

处理这个问题的惯用方法是什么?我对控制反转以及依赖注入和服务定位器等不同模式非常熟悉,但我无数次听到这不是惯用的。

【问题讨论】:

单元测试应该单独测试CreatePullRequest的逻辑,我对集成测试不感兴趣。 我的意思是这里没有逻辑可以测试——一切都发生在 github 包中,它有自己的测试。如果您确实在 ...s 后面隐藏了其他逻辑,则将其拆分为单独的部分进行测试。 @JimB 如果我将... 背后的逻辑拆分为自己的测试,我正在测试这些东西,而不是CreatePullRequest(...) 中的逻辑。我仍然想要一个单元测试来测试这个代码单元的逻辑流。 【参考方案1】:

Go 的一个重要设计特性是解耦 (Watch this great talk from Bill Kennedy about that topic)。在您的方法内部有一些依赖项,可以解耦。这种耦合方法使其无法真正进行测试。

你应该重构的东西:

tp := github.BasicAuthTransport:你不应该在你的方法中初始化授权。它应该作为参数移动到您的 gitHubService 中。在您的方法调用中,您可以通过s.tp 访问ist。您也可以将其作为方法的输入参数。 github.NewClient()client.Repositories.CreatePullRequest(...) 只需阅读来自 Peter Bourgon Make dependencies explicit! 的 golang 最佳实践。另一种方法是创建一个接口,其中包含所有被调用的函数。这个接口应该是你方法的输入。

解耦代码后,您可以非常轻松地模拟所有内容。如果您使用接口作为输入,您可以创建一个模拟结构,它实现了接口。如果使依赖项显式化,则可以覆盖它们。在最后一种情况下,用于存储调用值的代码不是那么干净,但它也可以工作。惯用的方式是使用接口。

【讨论】:

【参考方案2】:

我遇到了类似的问题,看起来,您唯一的选择是在其间设置另一层,该层将调用您的“不可模拟”客户端。

例如为了模拟 govmi 客户端(用于 golang 的 vmware 客户端 sdk),我必须有一个具有接口和结构的“myCustomClient”才能调用 govmi.Client.AnyMethod..

然后我可以为“myCustomClient”生成模拟。

mockgen -source myCustomClient.go -package myPackage -destination myCustomClientMock.go

您可以通过以下方式安装它:get get github.com/golang/mock

【讨论】:

以上是关于在 golang 中模拟外部结构依赖项的主要内容,如果未能解决你的问题,请参考以下文章

Go外部依赖包从vendor$GOPATH和$GOPATH/pkg/mod下的查找顺序

尝试在 CircleCI 上获取 golang 私有依赖项时的问题

golang依赖注入工具wire指南

Golang Dep:在一个源树中有多个二进制文件

golang结构体嵌套和用结构体实现模拟“继承”

Golang依赖包的各项指标分析总结