在 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下的查找顺序