Как я могу использовать отражение (во время выполнения) в Lua? - PullRequest
1 голос
/ 23 апреля 2019

Я пытаюсь выполнить модульное тестирование кода с помощью Busted (который я не писал, и мне не разрешено проводить рефакторинг в настоящее время по деловым причинам) в Lua, и в этом модуле классов нет ни внедрения, ни внедрения зависимостей.Итак, я хотел бы заменить некоторые модули, необходимые в верхней части файла, например, local log = require("path.to.module.logger"):new(), на смоделированный логгер, который я сделал для отслеживания количества вызовов методов, т.е. logger:trace(), например, с * 1003.* в Мокито на Яве.В Java я могу использовать Reflection.Utils для этой цели.Что эквивалентно в Lua, чтобы помочь сделать этот не тестируемый код тестируемым?

Я уже пытался создать глобальную переменную с тем же именем переменной log и установить ее равной моему макету, используя этот пример:https://www.lua.org/pil/14.2.html

local _M = {}

local log = require ("path.to.module.logger"): new ()

...

function _M.init (...) log: trace ("debug") # Мне бы хотелось, чтобы этот экземпляр журнала не был тем, что приведен выше, а тот, который я вставляю в модуль в конце времени выполнения

Ответы [ 3 ]

0 голосов
/ 23 апреля 2019

Сегодня я действительно смог найти ответ от коллеги. «Отражение» на самом деле невозможно, как Николь Болас предложил в своем ответе, однако, из документации lua (http://lua -users.org / wiki / ModulesTutorial ) мы узнаем, что:

Lua caches modules in the package.loaded table.

Это означает, что мы можем перезаписать таблицу package.loaded в нашем тесте с перебоями и, по существу, во время выполнения заменить зависимости в тесно связанном коде (так же, как в Mockito через внедрение зависимостей в Java). Например:

package.loaded["path.to.module.logger"] = my_logger заменит глобальную зависимость path.to.module.logger на my_logger при условии, что она придерживается того же контракта.

0 голосов
/ 23 апреля 2019

Я написал бы мой макет для логгера и установил бы его по тому же пути, что и у оригинального логгера, но в другом корневом каталоге. Затем для тестов добавьте папку с макетами в начале LUA_PATH

Пример: /tmp/a/package/logger.lua:

local _M = {}

_M.log = function()
  print "Original logger"
end

return _M

/ TMP / б / пакет / logger.lua:

local _M = {}

_M.log = function()
  print "Mocked logger"
end

return _M

и тест /tmp/test/logger_spec.lua:

describe("Test suite", function()
  it("Testing the mock", function ()

    local log = require("package.logger")

    log.log()
  end)
end)

если вы установили LUA_PATH для использования оригинала: export LUA_PATH="/tmp/a/?.lua;;" и звонок накрылся:

busted logger_spec.lua
Original logger
●
1 success / 0 failures / 0 errors / 0 pending : 0.000527 seconds

и теперь наведите LUA_PATH на ваш макет: export LUA_PATH="/tmp/b/?.lua;;"

и вызов снова вылетел

busted logger_spec.lua
Mocked logger
●
1 success / 0 failures / 0 errors / 0 pending : 0.000519 seconds
0 голосов
/ 23 апреля 2019

«Отражение» не совсем вещь в Lua, не в Java-смысле этого термина.В качестве языка, который использует Duck Typing , все очень открыто.Lua имеет только одну структуру данных: таблицу. Все в Lua происходит из таблиц.Модуль - это просто таблица, возвращаемая чанком, загруженным require.

. Содержимое и структуры данных за таблицей могут быть скрыты через метатаблицы, которые можно использовать для предотвращения обычного процесса итерации (pairs, ipairs и т. Д.) Для доступа к элементам в таблице.Тем не менее, вы всегда можете использовать getmetatable, чтобы извлечь сам метатабель и вызвать его;Вы можете даже прорваться через обычный способ скрыть метатаблицу с помощью debug.getmetatable.

При этом, поскольку Lua полагается на Duck Typing, а API-интерфейсы Lua довольно открыты, довольно трудно быть подробный об упаковке каждой отдельной функции и таблицы.

Например, допустим, вы хотите обернуть модуль.Это достаточно просто;просто создайте пустую таблицу, в которой есть метатаблица, метаметоды которой вызывают методы упакованного модуля.Это работает для прямых API модуля.

Но что произойдет, если один из этих API возвратит объект, который сам должен быть упакован?Как вы сможете отличить специализированный объект API от обычной таблицы?Не менее важно, если вы можете успешно определить, какие возвращаемые значения нужно переносить, как вы это сделаете?В конце концов, если они передают одну из ваших таблиц-оболочек в API-функцию-оболочку, теперь ей нужно развернуть эту таблицу, чтобы она могла передать упакованную таблицу фактической функции-оболочке.

...