0Pricing
Go Academy · 课时

测试替身:模拟对象与存根

使用接口提高可测试性并实现依赖注入

测试替身:模拟对象与存根 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 testing 编写单元测试
  2. 表驱动测试
  3. 测试替身:模拟对象与存根
  4. 测试覆盖率与 testify
← 返回 Go Academy