我是Lua新手。我正在使用Lunity和LeMock对BLa 5.1代码进行单元测试。
我的类是StorageManager。我正在对其load()方法进行单元测试,该方法从磁盘加载文件。我不希望我的单元测试依赖于实际磁盘上的实际文件来测试它。
所以,我正在尝试使用模拟对象注入I / O依赖项,并在测试期间验证对象的行为。我无法弄清楚如何使用I / O对象,其语法在我的基于模拟的单元测试和“真实代码”调用时都有效。
如何更改代码(最好是load()方法),以便在从单元测试调用时它将完成它的工作(没有模拟的那个是临时的,直到我弄明白它 - 它类似于以后实际调用被测方法的代码?)
注意1:如果您运行这些测试,请记住“无模拟”测试需要磁盘上的文件,其文件名与VALID_FILENAME匹配。
注2:我考虑过使用pcall的try / catch-like行为执行一行或另一行(参见storageManager.lua第11行和第12行)。假设这甚至可能,它感觉就像一个黑客,它捕获我以后可能抛出的错误(out of load())。如果你别无选择,请详细说明这个选项。
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
修改
这些类通过了两个测试。非常感谢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
答案 0 :(得分:1)
我认为你使问题变得更加困难。
假设模块storageManager.lua本身不是io
模块的本地化,那么您需要做的就是在运行测试时用模拟对象替换全局io
。
如果模块本地化io
对象以提高性能,则需要在加载模块之前注入新值io
。这可能意味着您需要调用require
部分测试用例设置(以及从package.loaded
和_G
删除模块的所有痕迹的匹配清理)以便它可以在不同的测试用例中进行不同的模拟。
WinImage
修改强>
通过本地化模块来提高性能,我指的是将模块的方法复制到模块名称空间中的局部变量的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
我可能无法在恰当的时刻恢复真实物体,这是未经测试的,但它应该指向正确的方向。