如何在 Lua 中使用反射(在运行时)?
我正在尝试在 Lua 中使用 Busted 进行单元测试(我不是作者,也不允许由于商业原因进行重构),但是该模块中没有类或依赖注入的概念。因此,我想替换文件顶部所需的一些模块,如 local log = require("path.to.module.logger"):new(),使用我制作的模拟日志记录器,以跟踪方法调用的次数,例如类似于 Java 中的 Mockito 的 times()。在 Java 中,我可以使用 Reflection.Utils 来实现这个目的。在 Lua 中等价的是什么,可以帮助使这个不可测试的代码可测试?
我已经尝试使用与此变量名称相同的全局变量,并使用此示例将其设置为我的模拟:https://www.lua.org/pil/14.2.html
local _M = {}
local log = require("path.to.module.logger"):new()
...
function _M.init(...) log:trace("debug")#我想要注入到运行时模块中的 log 实例不是上面的那个实例 end
我今天早上实际上从同事那里找到了一个答案。正如尼古拉·博拉斯在他的答案中建议的那样,“反射”实际上是不可能的。然而,从Lua文档(http://lua-users.org/wiki/ModulesTutorial)中我们可以了解到:
Lua将模块缓存在package.loaded表中。
这意味着我们可以在我们的破坏性测试中覆盖package.loaded表,从而在运行时替换紧密耦合的代码中的依赖项(就像在Java中通过依赖注入来模拟Mockito一样)。例如:
package.loaded["path.to.module.logger"] = my_logger将以全局方式将依赖项path.to.module.logger替换为my_logger,假设它遵循相同的协议。
我将编写模拟日志程序并将其设置在与原始日志程序相同的路径下,但在不同的目录根目录下。然后为测试添加带有模拟值的文件夹,放在LUA_PATH的开头。
例如:
/tmp/a/package/logger.lua:
local _M = {}
_M.log = function()
print "原始日志程序"
end
return _M
/tmp/b/package/logger.lua:
local _M = {}
_M.log = function()
print "模拟日志程序"
end
return _M
和测试/tmp/test/logger_spec.lua:
describe("测试套件", function()
it("测试模拟程序", function ()
local log = require("package.logger")
log.log()
end)
end)
如果将LUA_PATH设置为使用原始值:
export LUA_PATH="/tmp/a/?.lua;;"
并且调用busted:
busted logger_spec.lua
原始日志程序
â—
1 成功 / 0 失败 / 0 错误 / 0 挂起:0.000527 秒
现在将LUA_PATH指向您的模拟值:
export LUA_PATH="/tmp/b/?.lua;;"
并再次调用busted
busted logger_spec.lua
模拟日志程序
â—
1 成功 / 0 失败 / 0 错误 / 0 挂起:0.000519秒
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

"反射" 在 Lua 中并不存在,至少不是 Java 中所用的那个词。作为使用 鸭子类型 的语言,每一样东西都是非常开放的。Lua 只有一种数据结构:表格。在 Lua 中的 任何东西 都来源于表格。一个模块只是由
require加载的代码块返回的一个表格。通过元表,可以隐藏表格背后的内容和数据结构,这可以用来防止使用
pairs、ipairs等等常规的迭代方式访问表格中的元素。然而,在任何情况下,都可以通过getmetatable提取元表本身并调用它;甚至还可以使用debug.getmetatable打破通常隐藏元表的方式。话虽如此,由于 Lua 依赖鸭子类型,并且 Lua 的 API 是非常开放的,因此要完全包装每个函数和表格是相当困难的。
例如,假设你想包装一个模块。这很容易;只需创建一个空表格,其元表的元方法调用包装的模块方法即可。这对于模块的直接 API 可以起作用。
但如果其中一个 API 返回一个需要被包装的对象些呢?你如何区分特殊的 API 对象和普通的表格呢?同样重要的是,如果你能成功地识别需要被包装的返回值,你该怎么做呢?毕竟,如果他们通过一个你的包装表格传递给包装的 API 函数,现在它需要取消包装该表格,以便将包装的表格传递给实际被包装的函数。