Ложный импорт при юнит-тестировании кода Lua с Busted
Я очень новичок в 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
Есть идеи, что я здесь делаю не так?
0 ответов
Есть несколько небольших проблем, мешающих работе вашего кода: во-первых, вы не можете переопределить http
local, установив глобальную переменную с тем же именем. Локальный всегда будет затенять глобальную переменную.
Во-вторых, require все еще вызывается, и если вы удалили local
в тестовом коде он все равно перезапишет все, что хранится в глобальном http
переменная в то время.
Вам нужен способ сделать require
загрузи свой resty.fake_http
модуль при вызове как require "resty.http"
. Я могу думать о трех способах:
1. Предварительно загрузите модуль
В require
функция использует две таблицы package.loaded
а также package.preload
чтобы контролировать, когда и как загружаются модули (подробности здесь). когдаrequire
вызывается, сначала проверяется, package.loaded[module]
установлен, и если да, возвращает это значение.
Это первая возможность издеваться над модулем:
package.loaded["resty.http"] = require "resty.fake_http"
local app = require('app')
В качестве альтернативы, если нет записи в package.loaded
, package.preload[module]
проверяется на наличие функции, которая может загрузить модуль:
package.preload["resty.http"] = function ()
return require("resty.fake_http")
end
local app = require('app')
2. Измените package.path и заткните модуль
Вы уже делаете это, добавляя spec
каталог на путь. Все, что вам нужно сделать, это назвать свой поддельный модуль так же, как оригинал, и он будет загружен автоматически. например, test _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
функции, которые могут помочь вам в более сложных случаях в будущем.