如何让非面向对象的Lua模块支持依赖注入与单元测试?
解决Lua非面向对象模块的依赖注入与测试问题
好问题!要在保持非OO模块单例特性的同时实现依赖注入(DI)用于测试,我们可以给模块添加一个可配置依赖的入口方法,既不用把模块改成类,也能保留原有的单加载优势。
核心思路
模块默认加载真实的业务依赖,但额外提供一个仅用于测试的方法,允许替换内部的依赖实例;测试完成后还可以重置回默认依赖,避免影响其他测试场景。
具体实现代码
-- 模块默认加载真实的database依赖 local database = require 'database' local M = {} function M:GetData() return database:GetData() end -- 新增:用于测试时注入依赖的方法 function M:InjectDependency(newDatabase) database = newDatabase end -- 可选:重置回默认依赖,方便测试用例隔离 function M:ResetDependency() database = require 'database' end return M
方案优势
- 保持原有使用体验:平时业务代码中调用
require后,模块依然是单例,和原来的非OO写法完全一致,没有额外性能开销 - 支持测试场景:测试时只需调用
InjectDependency传入伪造的fake database,就能验证模块的逻辑是否正确调用了依赖的方法 - 避免重复创建模块实例:不像你之前尝试的闭包方案,每次调用都会生成新的模块副本,这个方案全程复用同一个模块实例
测试场景示例
-- 测试用例代码 local myModule = require 'myModule' -- 构造伪造的database local fakeDatabase = { GetData = function() return "mock test data" end } -- 注入假依赖 myModule:InjectDependency(fakeDatabase) -- 验证模块逻辑 assert(myModule:GetData() == "mock test data") -- 测试完成后重置依赖,避免影响其他用例 myModule:ResetDependency()
这个方案完美平衡了非OO模块的简洁性、单例特性,同时满足了测试阶段的依赖注入需求,不需要对原有模块结构做大幅改动。
内容的提问来源于stack exchange,提问作者Chris Tanner
相关产品推荐
相关产品推荐

