Dependency injection Lua I/O依赖项注入
我是个新手。我正在使用和对Lua5.1代码进行单元测试 我的班级是StorageManager。我正在对其load()方法进行单元测试,该方法从磁盘加载文件。我不想让依赖于实际磁盘上的实际文件的单元测试来测试它 因此,我尝试将I/O依赖项注入一个模拟对象,并在测试期间验证该对象的行为。我不知道如何使用I/O对象,该对象的语法在我的基于模拟的单元测试和“真实代码”调用时都能工作 我如何更改代码(最好是load()方法),以便在从任何一个单元测试调用它时都能完成它的工作(在我弄明白这一点之前,没有mock的代码是临时的——它类似于稍后实际调用被测方法的代码) 注1:如果运行这些测试,请记住“无模拟”测试要求磁盘上的文件名与有效的\u文件名匹配 注2:我考虑过使用pcall的类似try/catch的行为来执行一行或另一行(请参阅storageManager.lua第11行和第12行)。假设这是可能的,它感觉像是黑客攻击,它捕获了我以后可能想要抛出的错误(out-load())。如果没有其他选择,请详细说明这一选择 测试\u storageManager.lua:Dependency injection Lua I/O依赖项注入,dependency-injection,mocking,lua,Dependency Injection,Mocking,Lua,我是个新手。我正在使用和对Lua5.1代码进行单元测试 我的班级是StorageManager。我正在对其load()方法进行单元测试,该方法从磁盘加载文件。我不想让依赖于实际磁盘上的实际文件的单元测试来测试它 因此,我尝试将I/O依赖项注入一个模拟对象,并在测试期间验证该对象的行为。我不知道如何使用I/O对象,该对象的语法在我的基于模拟的单元测试和“真实代码”调用时都能工作 我如何更改代码(最好是load()方法),以便在从任何一个单元测试调用它时都能完成它的工作(在我弄明白这一点之前,没有m
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}
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
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}
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
编辑:
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}
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
测试\u 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}
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
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}
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
我认为你正在使这个问题变得更加困难 假设module storageManager.lua本身没有本地化
io
模块,那么您所需要做的就是在运行测试时用模拟对象替换全局io
如果模块确实将io
对象本地化以提高性能,那么您需要在加载模块之前注入io
的新值。这可能意味着您需要调用测试用例设置的require
部分(以及从包中删除模块所有痕迹的匹配清理。加载的和\G
),以便在不同的测试用例中对其进行不同的模拟。
WinImage
编辑:
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}
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
通过对模块进行性能本地化,我指的是将模块的方法复制到模块名称空间中的局部变量中的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对象”,而是我想“在运行测试时用[my]模拟对象替换全局io”。我以为我是在storageManager上做的。lua:4?如何正确地“替换全局io”?你能提供(或指给我)示例代码吗?你的编辑给了我所需要的。我已将更改后的代码编辑为原始问题。非常感谢。