8

我对 Lua 很陌生,我正在尝试测试我在 Nginx 服务器上运行的脚本。我被推荐 Busted 但我似乎无法弄清楚如何模拟一些本地进口。

Lua 代码导入以下内容:

local http = require "resty.http"

在测试 _spec 文件中,我是这样开始的:

package.path = "files/?.lua;spec/?.lua;" .. package.path

_G.http = require('resty.fake_http')
local app = require('app')

我在fake_http.lua里面创建了一个文件spec/resty/http

但是当我运行一个虚拟测试时,我收到以下错误:

suite spec/app_spec.lua
files/app.lua:3: module 'resty.http' not found:No LuaRocks module found for resty.http

知道我在这里做错了什么吗?

4

1 回答 1

7

有几个小问题会阻止您的代码工作:首先,您不能通过http设置同名的全局变量来覆盖本地。局部变量总是会影响全局变量。

其次,仍会调用 require ,如果您local在测试代码中删除了 ,它仍然会覆盖当时保存在全局http变量中的任何内容。

您需要的是一种在调用时require加载resty.fake_http模块的方法require "resty.http"。我能想到的方法有以下三种:

1. 预加载模块

require函数使用这两个表package.loadedpackage.preload控制何时以及如何加载模块(详情请点击此处)。当require被调用时,它首先检查是否package.loaded[module]设置,如果是,则返回该值。

这是模拟模块的第一个机会:

package.loaded["resty.http"] = require "resty.fake_http"
local app = require('app')

或者,如果 中没有条目package.loadedpackage.preload[module]则检查可以加载模块的函数

package.preload["resty.http"] = function ()
  return require("resty.fake_http")
end
local app = require('app')

2.更改package.path并破坏模块

您已经通过将spec目录添加到路径来执行此操作。您需要做的就是将您的假模块命名为与原始模块相同的名称,它将自动加载。例如测试_spec:

package.path = "files/?.lua;spec/?.lua;" .. package.path
local app = require('app')

在测试代​​码中,它会自动拾取spec/resty/http.lua

这两种解决方案之间的区别在于,第二种解决方案resty.fake_http在测试代码实际需要它时才需要,而第一种解决方案在任何情况下都需要它。

3.猴子补丁require

这是三个解决方案中最难看的一个,但它也可以正常工作。 require只是另一个全局变量,所以你也可以覆盖它:

local _original_require = require
function require(modname, ...)
  if modname == "resty.http" then
    -- implement the exception here
    return _original_require("resty.fake_http")
  end
  -- otherewise act as normal
  return _original_require(modname, ...)
end
local app = require('app')

第二种方法是最简单易懂的方法,但第一种方法更简洁、更通用。如果您能抽出 30 分钟的时间阅读链接的文档并了解其require功能,那么这可以帮助您应对未来更复杂的情况。

于 2019-05-02T10:16:05.410 回答