如何Mock ngx对象并测试OpenResty Lua文件?
嘿,这个问题我太熟了!在测试OpenResty的Lua代码时,Mock ngx对象是刚需——毕竟普通测试环境根本拿不到OpenResty原生的ngx上下文。下面我用社区常用的busted测试框架给你一步步拆解实现:
方法一:手动Mock全局ngx对象(适合简单场景)
首先,我们可以直接在测试文件里创建一个模拟的ngx表,替换全局的ngx对象,这样被测试代码里的ngx.var就会指向我们的Mock对象。
假设你的测试文件叫file_spec.lua,代码如下:
-- 1. 创建Mock的ngx对象,模拟ngx.var子表 local ngx_mock = { var = {} } -- 2. 把全局ngx替换成我们的Mock _G.ngx = ngx_mock -- 3. 清除模块缓存(如果之前加载过file.lua),然后重新加载触发赋值逻辑 package.loaded['file'] = nil require('file') -- 4. 写测试断言 describe('computeUpstream赋值逻辑', function() it('应该把计算结果正确赋值给ngx.var.upstream', function() -- 这里可以根据computeUpstream的实际逻辑,设置预期值 -- 比如如果computeUpstream固定返回"test_upstream",就断言这个值 assert.are.equal(ngx_mock.var.upstream, "test_upstream") end) end)
如果computeUpstream是复杂逻辑,你还可以临时重写它来测试边界场景:
-- 在测试前重写computeUpstream,返回我们预设的值 _G.computeUpstream = function() return "custom_test_upstream" end -- 重新加载文件触发赋值 package.loaded['file'] = nil require('file') -- 断言赋值结果 assert.are.equal(ngx_mock.var.upstream, "custom_test_upstream")
方法二:调整代码为模块结构(更灵活的测试方式)
如果你的代码是脚本形式(直接执行赋值),测试时需要反复加载模块,有点麻烦。建议把代码改成模块结构,这样可以单独测试computeUpstream函数,再验证赋值逻辑:
修改原file.lua为模块:
local M = {} -- 把computeUpstream作为模块方法导出,方便单独测试 function M.computeUpstream() -- 这里保留你的复杂计算逻辑 return "theCalculatedUpstream" end -- 只有在OpenResty环境(存在ngx对象)时,才执行赋值 if ngx then ngx.var.upstream = M.computeUpstream() end return M
然后测试文件可以这样写:
describe('file模块测试', function() local ngx_mock local file_module -- 每次测试前初始化Mock和模块 before_each(function() ngx_mock = { var = {} } _G.ngx = ngx_mock package.loaded['file'] = nil file_module = require('file') end) -- 测试computeUpstream本身的逻辑 it('computeUpstream应该返回正确的计算结果', function() local result = file_module.computeUpstream() -- 根据你的计算逻辑设置预期值 assert.are.equal(result, "theCalculatedUpstream") end) -- 测试赋值逻辑 it('应该把computeUpstream的结果赋值给ngx.var.upstream', function() -- 手动执行赋值逻辑(因为模块加载时已经执行过,或者可以Mock后重新加载) ngx_mock.var.upstream = file_module.computeUpstream() assert.are.equal(ngx_mock.var.upstream, file_module.computeUpstream()) end) end)
方法三:用luassert.mock优雅Mock(推荐)
busted自带的luassert.mock工具可以更简洁地创建Mock对象,还能验证ngx对象的方法调用(如果你的代码用到了ngx.log这类API的话):
测试代码示例:
local luassert = require('luassert') describe('file.lua 高级测试', function() local ngx_mock before_each(function() -- 创建一个Mock的ngx对象,自动补全不存在的字段 ngx_mock = luassert.mock(_G.ngx or {}, { create = true }) ngx_mock.var = {} _G.ngx = ngx_mock package.loaded['file'] = nil end) after_each(function() -- 测试后还原全局ngx,避免影响其他测试 _G.ngx = nil end) it('正确赋值ngx.var.upstream', function() local file_module = require('file') local expected_value = "mock_upstream" -- Mock computeUpstream的返回值 file_module.computeUpstream = function() return expected_value end -- 执行赋值逻辑 ngx.var.upstream = file_module.computeUpstream() -- 断言赋值结果 assert.are.equal(ngx_mock.var.upstream, expected_value) end) end)
内容的提问来源于stack exchange,提问作者Kousha
相关产品推荐
相关产品推荐

