单元测试中Mock外部todo库的Client及perform方法遇到难题
搞定外部包Client的Mock单元测试,看这篇就够了
我来帮你解决这个Mock外部todo包的难题!首先看你的代码,问题出在tryGet()里直接硬实例化了todo.Client,这种紧耦合的写法让你没法轻松替换成Mock对象。咱们通过接口抽象+依赖注入的方式来解耦,再配合Mock库搞定测试,一步步来:
1. 先给外部依赖做个接口抽象
首先,为todo.Client里你用到的perform方法定义一个接口——这样咱们的代码就只依赖接口,不依赖具体的外部实现:
// 定义接口,和todo.Client的perform方法签名保持一致 // 注意:你原代码里todo.Client的perform是无参,但tryGet里传了参数,应该是笔误,先把签名对齐 type TodoClient interface { perform(method string, header http.Header) error }
2. 修改config结构体,依赖接口而非具体类型
把原来的todo.Client字段换成咱们刚定义的接口类型,这样后续就能灵活替换成Mock实例了:
type config struct { Key string `json:"key"` Client TodoClient `json:"client"` // 用接口替代具体的todo.Client }
3. 调整tryGet方法,让依赖可注入
原来的tryGet里自己创建tClient的写法太死板,现在改成直接用config里的Client字段——这样测试时就能把Mock对象塞进去了:
func (c *config) tryGet() error { // 直接用注入好的Client,不用自己硬创建了 err := c.Client.perform("GET", header) // 假设header是方法内已定义的变量 return err // 原代码返回nil,记得改成返回实际的错误哦 }
如果需要默认的真实客户端,咱们可以在初始化的时候设置:
var instance *config // 初始化默认实例,用真实的todo.Client func init() { httpClient := &http.Client{} realTodoClient := todo.Client{BaseURL: "http://url", HTTPClient: httpClient} instance = &config{ Key: "Key", Client: &realTodoClient, } }
4. 用Mock库创建TodoClient的Mock实现
咱们用常用的testify/mock库来做Mock,先定义一个Mock结构体实现TodoClient接口:
import "github.com/stretchr/testify/mock" // MockTodoClient 是TodoClient接口的Mock实现 type MockTodoClient struct { mock.Mock } // perform 实现接口方法,把调用转发给Mock的Called方法 func (m *MockTodoClient) perform(method string, header http.Header) error { args := m.Called(method, header) return args.Error(0) }
5. 编写单元测试,用Mock替代真实客户端
现在就可以写测试了,咱们预设Mock的行为,验证tryGet的逻辑是否正确:
import ( "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) func TestTryGet(t *testing.T) { // 1. 创建Mock客户端实例 mockTodoClient := new(MockTodoClient) // 2. 预设调用行为:当perform被传入"GET"和指定header时,返回nil(或者你想要的错误) testHeader := http.Header{} mockTodoClient.On("perform", "GET", testHeader).Return(nil) // 3. 创建config实例,注入Mock客户端 testConfig := &config{ Key: "test-key", Client: mockTodoClient, } // 4. 调用要测试的tryGet方法 err := testConfig.tryGet() // 5. 断言结果:没有错误 assert.NoError(t, err) // 6. 验证Mock方法是否按预期被调用了 mockTodoClient.AssertExpectations(t) }
小提醒
- 你原代码里
todo.Client的perform方法签名和tryGet里的调用不匹配(原perform无参,调用传了两个参数),记得先修正这个笔误,不然接口和实现对不上哦。 - 如果没法修改外部
todo包的代码,这种接口抽象的方式是最通用的解耦方案,不管外部包怎么变,只要接口不变,咱们的代码和测试都不用改。
内容的提问来源于stack exchange,提问作者MANOJ
相关产品推荐
相关产品推荐

