在Go中模拟非接口类型

Roo*_*osh 5 unit-testing mocking go

首先,我要说我是Go语言的新手,因此在与其他库一起使用时,我正在寻找模拟技术。我很清楚接口和依赖注入是使代码可测试和可模拟的最佳方法。

在使用第三方客户端库(Google云存储)时,尝试模拟其客户端的实现时遇到了问题。主要问题是客户端库中的类型未使用接口实现。我可以生成模仿客户端实现的接口。但是,某些函数的返回值将返回指向基础结构类型的指针,由于私有属性,这些指针很难或无法模拟。这是我要解决的问题的样本:

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

这是带注释的示例,其中包含我要解决的问题。

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{}
}
Run Code Online (Sandbox Code Playgroud)

这甚至是可笑的情况吗?是否有特殊的技术可以解决该库不提供任何接口作为返回类型的事实?

Mom*_*sov 6

通常,在处理对测试不友好的第三方库时,您可以采用的一种方法是使用中间层将第三方代码抽象出来。

// mock and use this interface
type IntermediateLayer interface {
    DoSomething()
}

type intermediateImplementation struct{}

func (i intermediateImplementation) DoSomething() {
    client := &ThirdPartyClient{}
    underlyingValue := client.SomeFunction()
    underlyingValue.SomeOtherFunction()
}
Run Code Online (Sandbox Code Playgroud)

您可以模拟IntermediateLayer接口并测试使用它的业务代码。您将需要创建一个结构来实现IntermediateLayer接口并使用第三方 API 来实现您的目标。

然后,问题将转移到测试IntermediateLayer. 根据使用第三方库的代码的复杂程度,您可以选择不测试它或将其留给更高级别的测试(如集成测试)来验证它。

走这条路的一个好处是,您可以将业务代码与第三方库分离,这使您可以在将来的某个时候切换到不同的第三方库,而无需重新编写所有代码。即使在处理对测试友好的第三方库时,您甚至可以考虑使用这种方法,代价是更多的抽象和样板代码。


apx*_*pxp 2

你的问题的答案是:是的,这就是你可以做到的方式。

但你问错了问题。你不应该问你如何嘲笑某些东西。因为,你什么时候需要模拟?

只是为了测试。所以你应该举一个你想要测试的具体例子。

当您使用外部包时,您有两种可能性。您想要测试外部包的行为是否符合您的预期,或者您信任该外部包并且您只是在测试您的代码。

因此,当您测试代码时,您需要测试客户端调用是否正确。所以你的模拟对于这种情况来说是可以的。请记住,重要的是您正在测试的内容,而不是您是否可以模拟某些内容。