2009-06-20 4 views
0

Я новичок в Lua. Я тестирую модуль Lua 5.1 с использованием Lunity и LeMock.Взаимодействие зависимостей Lua I/O

Мой класс - StorageManager. Я тестирую модуль методом load(), который загружает файлы с диска. Я не хочу, чтобы мои модульные тесты зависели от фактических файлов на реальном диске, чтобы проверить это.

Итак, я пытаюсь ввести зависимость ввода-вывода с макетным объектом и проверить поведение объекта во время моего теста. Я не могу понять, как использовать объект ввода-вывода с синтаксисом, который работает, когда вызывается как моим модульным модульным тестом, так и «реальным кодом».

Как я могу изменить код (метод load(), желательно), чтобы он выполнял свою работу при вызове из одного модульного теста (один без макета является временным, пока я не получу этот результат - он напоминает код, который позже на самом деле вызовет тестируемый метод)?

Примечание1: Если вы запустите эти тесты, помните, что тест «без макета» ожидает файл на диске, имя файла которого соответствует VALID_FILENAME.

Note2: Я думал об использовании поведения try/catch-like pcall для выполнения одной строки или другой строки (см. Строки storageManager.lua 11 & 12). Предполагая, что это даже возможно, это похоже на взлом, и он ловушки ошибок, которые я мог бы позже захотеть выбросить (из-за нагрузки()). Пожалуйста, изложите эту опцию, если вы не видите альтернативы.

test_storageManager.lua:

1 require "StorageManager" 
2 require "lunity" 
3 require "lemock" 
4 module("storageManager", package.seeall, lunity) 
5 
6 VALID_FILENAME = "storageManagerTest.dat" 
7 
8 function setup() 
9  mc = lemock.controller() 
10 end 
11 
12 function test_load_reads_file_properly() 
13  io_mock = mc:mock() 
14  file_handle_mock = mc:mock() 
15  io_mock:open(VALID_FILENAME, "r");mc:returns(file_handle_mock) 
16  file_handle_mock:read("*all") 
17  file_handle_mock:close() 
18  mc:replay() 
19  storageManager = StorageManager:new{ io = io_mock } 
20  storageManager:load(VALID_FILENAME) 
21  mc:verify() 
22 end 
23 
24 function test_load_reads_file_properly_without_mock() 
25  storageManager = StorageManager:new() 
26  storageManager:load(VALID_FILENAME) 
27 end 
28 
29 runTests{useANSI = false} 

storageManager.lua:

1 StorageManager = {} 
2 
3 function StorageManager.new (self,init) 
4  init = init or { io=io } -- I/O dependency injection attempt 
5  setmetatable(init,self) 
6  self.__index = self 
7  return init 
8 end 
9 
10 function StorageManager:load(filename) 
11  file_handle = self['io'].open(self['io'], filename, "r") -- works w/ mock 
12  -- file_handle = io.open(filename, "r") -- works w/o mock 
13  result = file_handle:read("*all") 
14  file_handle:close() 
15  return result 
16 end 

Edit:

Эти классы проходят оба теста. Огромное спасибо RBerteig.

test_storageManager.lua

1 require "admin.StorageManager" 
2 require "tests.lunity" 
3 require "lib.lemock" 
4 module("storageManager", package.seeall, lunity) 
5 
6 VALID_FILENAME = "storageManagerTest.dat" 
7 
8 function setup() 
9  mc = lemock.controller() 
10 end 
11 
12 function test_load_reads_file_properly() 
13  io_mock = mc:mock() 
14  file_handle_mock = mc:mock() 
15  io_mock.open(VALID_FILENAME, "r");mc:returns(file_handle_mock) 
16  file_handle_mock:read("*all") 
17  file_handle_mock:close() 
18  mc:replay() 
19  local saved_io = _G.io 
20  _G.io = io_mock 
21  package.loaded.io = io_mock 
22  storageManager = StorageManager:new() 
23  storageManager:load(VALID_FILENAME) 
24  _G.io = saved_io 
25  package.loaded.io = saved_io 
26  mc:verify() 
27 end 
28 
29 function test_load_reads_file_properly_without_mock() 
30  storageManager = StorageManager:new() 
31  storageManager:load(VALID_FILENAME) 
32 end 
33 
34 runTests{useANSI = false} 

storageManager.lua

1 StorageManager = {} 
2 
3 function StorageManager.new (self,init) 
4  init = init or {} 
5  setmetatable(init,self) 
6  self.__index = self 
7  return init 
8 end 
9 
10 function StorageManager:load(filename) 
11  file_handle = io.open(filename, "r") 
12  result = file_handle:read("*all") 
13  file_handle:close() 
14  return result 
15 end 

ответ

1

Я думаю, что вы делаете проблема сложнее, чем она должна быть.

Предполагая, что модуль storageManager.lua сам не локализует модуль io, тогда все, что вам нужно сделать, это заменить глобальный io вашим макетным объектом во время выполнения теста.

Если модуль выполняет локализацию объекта io для обеспечения производительности, вам необходимо будет ввести новое значение io перед загрузкой модуля. Это может означать, что вам нужно сделать вызов в настройке тестового примера (и соответствующую очистку, которая удаляет все следы модуля с package.loaded и _G), чтобы его можно было по-разному оценивать в разных тестовых случаях. WinImage

Edit:

Локализуя модуль для исполнения я имею в виду Lua идиома копирование методы модуля и локальных переменных в пространстве имен модуля. Например:

 
-- somemodule.lua 
require "io" 
require "math" 

-- copy io and math to local variables 
local io,math=io,math 

-- begin the module itself, note that package.seeall is not used so globals are 
-- not visible after this point 
module(...) 

function doMathAndIo() 
    -- does something interesting here 
end 

Если вы сделаете это, то ссылки на фондовых модули io и math сделаны в тот момент, когда require "somemodule" выполняется. Замена любого из этих модулей после вызова require() с измененной версией не будет эффективной.

Для эффективного издевательства над модулем, который используется с этой идиомой, вы должны были бы иметь макет объекта на месте до вызов require().

Вот как я бы идти о замене объекта Io на время вызова в тестовом примере:

 

function test_load_reads_file_properly() 
    io_mock = mc:mock() 
    file_handle_mock = mc:mock() 
    io_mock:open(VALID_FILENAME, "r");mc:returns(file_handle_mock) 
    file_handle_mock:read("*all") 
    file_handle_mock:close() 
    mc:replay() 

    local saved_io = _G.io 
    _G.io = io_mock 
    package.loaded.io = io_mock 
    storageManager = StorageManager:new{ } 
    storageManager:load(VALID_FILENAME) 
    _G.io = saved_io 
    package.loaded.io = saved_io 
    mc:verify() 
end 

не может быть восстановление реального объекта точно в нужный момент, и это непроверено, но оно должно указывать на вас в правильном направлении.

+0

Я не имею в виду (или не знаю, как) «локализовать объект io для производительности», но я хочу «заменить глобальный io на [мой] макет объекта при запуске теста». Я думал, что делаю это на storageManager.lua: 4? Как правильно «заменить глобальную io»? Можете ли вы предоставить пример кода (или указать мне)? – lance

+0

Ваше редактирование дало мне то, что мне было нужно. Я редактировал измененный код в исходный вопрос. Большое спасибо. – lance

 Смежные вопросы

  • Нет связанных вопросов^_^