【问题标题】:Mocking a type where interface signature's types are also mocked模拟接口签名的类型也被模拟的类型
【发布时间】:2025-11-25 19:35:02
【问题描述】:

我正在尝试围绕我不想编辑的现有库编写一个包装器,同时允许通过模拟进行测试。

为了允许测试和模拟,我创建了满足现有库的类型签名的接口,并且正在使用 mockgen。我还必须模拟这些接口类型签名中的函数返回的一些类型。

但是,这样做会导致接口不再满足现有库。

示例:

我有一个ClientType,它有一个函数Subscribe,它返回一个SubscriptionType

func NewClientType() ClientType {
    return ClientType{"project name"}
}

type ClientType struct {
    projectID string
}

func (l *ClientType) Subscribe(subID string) SubscriptionType {
    return SubscriptionType{subID}
}

type SubscriptionType struct {
    subscriptionID string
}

func (t *SubscriptionType) Receive() {
    fmt.Println("Stuff")
}

我想模拟Receive 方法。

这将允许我创建一个客户端,为它订阅一个主题,然后在测试中模拟这个接收消息。

我试过了:

type MyLibraryWrapper struct {
    ClientType
}

type MockedClientType interface {
    Subscribe(string) MockedSubscriptionType
}

type MockedSubscriptionType interface {
    Receive()
}

func main() {
    client := NewClientType()

    myMockedType := NewWrapper(client)
    sub := myMockedType.Subscribe("a")
    _ = sub // use it
}

func NewWrapper(client ClientType) MyLibraryWrapper {
    return MyLibraryWrapper{client}
}

这给了:

cannot use client (type ClientType) as type MockedClientType in argument to NewWrapper:
    ClientType does not implement MockedClientType (wrong type for Subscribe method)
        have Subscribe(string) SubscriptionType
        want Subscribe(string) MockedSubscriptionType

现场演示:

https://go.dev/play/p/6Pr_Y4VtOAW

【问题讨论】:

    标签: unit-testing go mocking


    【解决方案1】:

    您的库 API 被硬编码为返回 structs。您必须先将其每个 struct 包装在一个接口中,然后才能模拟任何内容。

    嵌入该任务变得更容易。

    type MyLibraryWrapper struct {
        ClientType
    }
    
    type MyClientType interface {
        Subscribe(string) MySubscriptionType
    }
    
    type MySubscriptionType interface {
        Receive()
    }
    
    func NewWrapper(client ClientType) MyClientType {
        return MyLibraryWrapper{client}
    }
    

    现在你可以模拟它了...

    type MockLibraryWrapper struct {}
    
    type MockSubscriptionType struct {}
    
    func (MyMockLibraryWrapper) Subscribe(string) MySubscriptionType {
        return MockSubscriptionType{}
    }
    
    func (MockSubscriptionType) Receive() {}
    
    func NewMockClientType() MyClientType {
        return MockLibraryWrapper{}
    }
    

    示例用法:

    func main() {
        realClient := NewWrapper(NewClientType())
        sub := realClient.Subscribe("a")
        sub.Receive()
    
        mockClient := NewMockClientType()
        sub := mockClient.Subscribe("a")
        sub.Receive()
    }
    

    【讨论】: