测试替身:模拟对象与存根
使用接口提高可测试性并实现依赖注入
测试替身:模拟对象与存根 是 CoddyKit 上的免费 Go Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
测试替身概览
测试替身会在测试中替代真实依赖。类型包括:存根(返回预设响应)、模拟对象(验证交互)、间谍(记录调用)、伪实现(可工作的内存实现)。
接口支持测试替身
设计函数时应让其接收接口,而不是具体类型。在生产环境中传入真实实现;在测试中传入测试替身。
type UserStore interface {
FindByID(id int) (*User, error)
}
func GetUser(store UserStore, id int) (*User, error) {
return store.FindByID(id)
}手动存根
存根是一个实现接口并返回预先配置值的简单结构体:
type stubStore struct{ user *User; err error }
func (s *stubStore) FindByID(_ int) (*User, error) { return s.user, s.err }
// In test:
store := &stubStore{user: &User{Name: "Alice"}}
result, _ := GetUser(store, 1)手动模拟对象
模拟对象会记录调用并对其进行断言:
type mockStore struct {
called bool
gotID int
user *User
}
func (m *mockStore) FindByID(id int) (*User, error) {
m.called = true; m.gotID = id
return m.user, nil
}
// Assert: if !mock.called { t.Error(...) }testify/mock
testify/mock 包可以生成或编写模拟对象,并配置调用预期和返回值:
type MockStore struct { mock.Mock }
func (m *MockStore) FindByID(id int) (*User, error) {
args := m.Called(id)
return args.Get(0).(*User), args.Error(1)
}
// In test:
m := new(MockStore)
m.On("FindByID", 1).Return(&User{}, nil)
m.AssertExpectations(t)gomock
go.uber.org/mock/gomock(以前称为 golang/mock)可以根据接口生成模拟对象。使用 mockgen CLI 生成模拟对象;使用 EXPECT() 设置预期。
伪实现
伪实现是一种可工作的内存实现。例如,使用映射存储 users 的内存用户存储。伪实现编写起来更复杂,但比模拟对象更贴近实际,也更不容易因实现变化而失效。
type fakeStore struct{ users map[int]*User }
func (f *fakeStore) FindByID(id int) (*User, error) {
u, ok := f.users[id]
if !ok { return nil, ErrNotFound }
return u, nil
}用于 HTTP 客户端的 httptest
net/http/httptest 提供进程内 HTTP 测试服务器和记录器,是 HTTP 依赖的标准伪实现。
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(User{Name: "Alice"})
}))
defer ts.Close()何时使用不同的测试替身
存根:简单的固定响应。模拟对象:验证调用次数和参数。伪实现:复杂行为。避免过度使用模拟对象——在集成测试中,伪实现和真实组件更可靠。
避免脆弱的测试
断言确切调用顺序或参数值的模拟对象,会随着实现演变而变得脆弱。在可行时,优先使用带输出断言的存根或伪实现。
发现接口
在确定调用方使用的具体类型的功能范围后再提取接口(ISP)。Go 支持隐式接口实现——无需修改具体类型。
快速检查
存根和模拟对象的主要区别是什么?
回顾:测试替身
要点:
- 针对接口设计,以支持测试替身
- 存根:返回预设数据;模拟对象:验证交互
- 使用 httptest 测试 HTTP;使用内存伪实现测试存储
- 避免过度使用模拟对象——复杂行为优先使用伪实现
常见问题解答
「测试替身:模拟对象与存根」课时是免费的吗?
是的 — 「测试替身:模拟对象与存根」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「测试替身:模拟对象与存根」这节课中我会学到什么?
使用接口提高可测试性并实现依赖注入 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「测试替身:模拟对象与存根」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 testing 编写单元测试
- 表驱动测试
- 测试替身:模拟对象与存根
- 测试覆盖率与 testify