模拟对象与间谍
隔离正在测试的代码。
模拟对象与间谍 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
测试替身
有时,函数会依赖速度较慢或外部的对象,例如网络调用或日志记录器。测试替身会用可控的替代对象替换这些依赖项。
busted 提供三种测试替身:间谍对象、存根和模拟对象。每一种都用于满足不同的需求。
间谍对象的作用
间谍对象会包装一个函数,并记录它的调用方式,同时仍然运行原函数。这样,您就可以验证某个对象是否被调用。
您可以使用 spy.new 包装一个函数来创建间谍对象,也可以监视表中的字段。
local s = spy.new(function() end)
s(1, 2)
assert.spy(s).was.called()监视方法
使用 spy.on 监视现有表中的方法。原方法仍会执行,但 busted 会跟踪每次调用。
这非常适合确认副作用是否发生,例如日志记录器是否被调用。
spy.on(logger, "info")
service.run()
assert.spy(logger.info).was.called()检查调用次数
间谍对象会记录自身被调用的次数。使用 was.called(n) 可以断言确切的调用次数。
这样可以发现函数调用次数过多或完全没有调用等错误。
assert.spy(s).was.called(2)
assert.spy(s).was_not.called()检查调用参数
您可以使用 was.called_with 断言间谍对象收到的参数。busted 会将记录的参数与您的预期进行比较。
这不仅能验证函数是否运行,还能验证它是否以正确方式运行。
local s = spy.new(function() end)
s("user", 42)
assert.spy(s).was.called_with("user", 42)存根的作用
存根会完全替换一个函数,因此原函数不会运行。您可以用它跳过速度较慢或不希望执行的操作,例如真实的网络或磁盘访问。
在表字段上使用 stub 可以创建存根。默认情况下,存根不执行任何操作,并返回 nil。
stub(api, "fetch")
service.load()
assert.stub(api.fetch).was.called()存根的返回值
您可以使用 returns 让存根返回固定值。这会将受控数据传入正在测试的代码。
这样,测试就完全不依赖真实的依赖项了。
stub(api, "fetch").returns({id = 1})
local r = service.load()
assert.are.same({id = 1}, r)模拟对象的作用
模拟对象会接收一个现有表,并一次性将其中所有函数转换为间谍对象或存根。这是跟踪整个对象的便捷方式。
调用 mock(obj) 来包装该对象,然后分别对各个方法进行断言。
local m = mock(realLogger)
app.start(m)
assert.spy(m.warn).was.called()使用存根方法的模拟对象
将 true 作为 mock 的第二个参数传入,会为每个方法创建存根,因此原方法都不会运行。这样可以完全隔离该对象。
当真实方法具有必须避免的副作用时,请使用这种方式。
local db = mock(database, true)
db.query.returns({})
assert.are.same({}, repo.all())恢复测试替身
间谍对象、存根和模拟对象会修改共享表,因此您必须恢复它们,否则后续测试会看到测试替身。请调用 :revert() 或 mock.revert。
将恢复操作放在 after_each 中,以确保测试之间的状态保持干净。
after_each(function()
mock.revert(m)
end)选择合适的测试替身
使用间谍对象观察真实函数,使用存根替换函数并提供受控行为,使用模拟对象一次性包装整个对象。
请优先选择能够完成任务的最轻量测试替身。过度使用模拟对象会让测试变得脆弱,并掩盖真实错误。
describe("mailer", function()
it("sends once", function()
local s = spy.on(smtp, "send")
mailer.notify("a@b.com")
assert.spy(s).was.called(1)
end)
end)快速检查
测试您对 busted 测试替身的理解。
回顾
测试替身可以将代码与速度较慢或外部的依赖项隔离开来。间谍对象在运行原函数的同时记录调用,存根用受控行为替换函数,而模拟对象会包装整个对象。
使用 was.called 和 was.called_with 进行断言,并始终在 after_each 中使用 revert 恢复测试替身,以保持测试隔离。
常见问题解答
「模拟对象与间谍」课时是免费的吗?
是的 — 「模拟对象与间谍」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「模拟对象与间谍」这节课中我会学到什么?
隔离正在测试的代码。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「模拟对象与间谍」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 为什么要测试
- describe 和 it 代码块
- 断言
- 模拟对象与间谍