【发布时间】:2017-07-30 18:22:42
【问题描述】:
让我先说我对 Go 还很陌生,所以在使用其他库时我正在寻找模拟技术。我很清楚,接口和依赖注入是保持代码可测试和可模拟的最佳方式。
在使用第 3 方客户端库(Google Cloud Storage)时,我在尝试模拟其客户端的实现时遇到了问题。主要问题是客户端库中的类型没有使用接口实现。我可以生成接口来模拟客户端实现。但是,一些函数的返回值返回指向底层结构类型的指针,由于私有属性,这些类型很难或不可能模拟。这是我要解决的问题的示例:
package third_party
type UnderlyingType struct {
secret string
}
type ThirdPartyClient struct {}
func (f *ThirdPartyClient) SomeFunction() *UnderlyingType {
return &UnderlyingType{
secret: "I can't mock this, it's a secret to the package"
}
}
这是一个带注释的示例,其中包含我要解决的问题。
package mock
// Create interface that matches third party client structure
type MyClientInterface interface {
SomeFunction() *third_party.UnderlyingType
}
type MockClient struct {
third_party.Client
}
// Forced to return the third party non-interface type 'UnderlyingType'
func (f *MockClient) SomeFunction() *UnderlyingType {
// No way to mock the value of the 'secret' property outside
// of the third-party package. Any underlying methods that
// depend on a non-nil reference to 'secret' will explode
// with the mock.
//
// TODO: Find a way to mock the 'secret' value
return &UnderlyingType{}
}
这甚至是一个可模拟的场景吗?是否有特殊技术可以解决库不提供接口作为返回类型这一事实?
【问题讨论】:
-
UnderlyingType可以做什么?它是否具有导出的字段或方法?或者它只是你传递给包中其他函数的东西?如果secret在包内部,您似乎不需要模拟它进行测试。 -
在我的例子中,UnderlyingType 是 io.Reader 的一个实现。 UnderlyingType 的“秘密”是对客户端的引用。由于我无法为模拟客户端实例分配“秘密”,因此我无法对 UnderlyingType 的客户端引用执行模拟操作或断言。
-
回答您关于导出方法的问题,是的。导出的方法尝试引用私有成员。由于我的模拟客户端是由实际实现组成的,因此模拟的导出函数最终将引用 nil,因为我无法设置私有成员。
-
返回
UnderlyintType的“模拟”不能解决问题吗?如果它是io.Reader,并且它是您的应用运行所需的全部,那么您可能想要返回io.Reader而不是UnderlyingType。 -
如果您正在使用this,那么您可能需要考虑在其之上构建一个抽象并将
storage.Client视为实现细节,有点像您将在顶部构建存储库Go 的database/sql包... 例如如果你想要的只是存储和检索文件,那么创建你自己的类型Store来处理io.Readers/io.Writers 并且其实现使用谷歌存储,但是在你的测试中你可以模拟你的@ 987654335@ 类型而不是整个cloud.google.com/go/storage包。
标签: unit-testing go mocking