I'm very new to Lua and I'm trying to test a script I have running with an Nginx server. I've been recommended Busted but I can't seem to figure out how to mock some of the local imports.
The Lua code imports the following:
local http = require "resty.http"
And in the test _spec file, I start off like this:
package.path = "files/?.lua;spec/?.lua;" .. package.path
_G.http = require('resty.fake_http')
local app = require('app')
I created a fake_http.lua
file inside spec/resty/http
.
But when I run a dummy test, I get the following error:
suite spec/app_spec.lua
files/app.lua:3: module 'resty.http' not found:No LuaRocks module found for resty.http
Any Idea what I'm doing wrong here?
There are a couple of small issues preventing your code from working:
First, you cannot override the http
local by setting a global variable of the same name. The local will always shadow the global variable.
Secondly, require is still being called and if you removed the local
in the test code it would still overwrite whatever was held in the global http
variable at that time.
What you need is a way to make require
load your resty.fake_http
module when called as require "resty.http"
. There are three ways I can think of:
The require
function uses the two tables package.loaded
and package.preload
to control when and how modules are loaded (details here).
When require
is called, it first checks whether package.loaded[module]
is set, and if so, returns that value.
This is the first opportunity for mocking the module:
package.loaded["resty.http"] = require "resty.fake_http"
local app = require('app')
Alternatively, if there is no entry in package.loaded
, package.preload[module]
is checked for a function that can load the module:
package.preload["resty.http"] = function ()
return require("resty.fake_http")
end
local app = require('app')
You are already doing this by adding the spec
directory to the path. All you need to do is name your fake module the same as the original, and it will be loaded automatically.
e.g. test _spec:
package.path = "files/?.lua;spec/?.lua;" .. package.path
local app = require('app')
In the tested code, it will pick up spec/resty/http.lua
automatically.
The difference between these two solutions is that the second one will only require resty.fake_http
if the tested code actually requires it, while the first one requires it in any case.
require
This is the ugliest of the three solutions, but it also works fine.
require
is just another global variable, so you can overwrite it as well:
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')
The second way is the simplest to do and understand, but the first is even cleaner and more versatile. If you can spare the 30 minutes to read the linked documentation and learn how require
functions, that can help you in more complex cases for the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With