Lua I injecção dependência / S
-
06-07-2019 - |
Pergunta
Eu sou um novato Lua. Estou unidade testar Lua 5.1 código usando LUNITY e LeMock .
A minha turma é StorageManager. Estou unidade testar seu método load (), que carrega os arquivos do disco. Eu não quero que minha unidade testes dependem dos arquivos reais no disco real para testar isso.
Então, eu estou tentando injetar o I / O de dependência com um objeto de simulação e verificar o comportamento desse objeto durante o meu teste. Eu não consigo descobrir como usar o objeto de E / S com uma sintaxe que funciona quando chamado por tanto o meu teste de unidade com base em simulação e o "código real".
Como posso alterar o código (o método load (), de preferência) para que ele irá fazer o seu trabalho quando chamado a partir de qualquer teste de unidade (um sem o simulado é temporário até que eu obter este descobriu - se assemelha a código que irá mais tarde, na verdade, chamar o método em teste)?
Nota 1:. Se você executar esses testes, lembre-se que o teste "sem falsa" espera um arquivo no disco cujo nome corresponde VALID_FILENAME
Nota 2: Pensei em usar try / catch-como comportamento para executar uma linha de pcall ou o outro (ver linhas storageManager.lua 11 e 12). Assumindo que é mesmo possível, ela se sente como um hack, e armadilhas erros que eu poderia mais tarde Quer jogadas (de carga ()). Por favor expor sobre esta opção se você não vê nenhuma alternativa.
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
Editar:
Essas classes passar ambos os testes. Muitos graças ao 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
Solução
Eu acho que você está fazendo o problema mais difícil do que tem que ser.
Assumindo que o módulo storageManager.lua não em si é localizar o módulo io
, então tudo que você precisa fazer é substituir o io
global com o seu objeto fictício durante a execução do teste.
Se o módulo faz localizar o objeto io
para o desempenho, então você precisa injetar o novo valor de io
antes de carregar o módulo. Isso pode significar que você precisa para fazer a chamada para parte require
da configuração do caso de teste (e uma limpeza correspondente que remove todos os traços do módulo de package.loaded
e _G
) para que ele possa ser ridicularizado diferente em diferentes casos de teste.
WinImage
Editar:
Ao localizar um módulo para o desempenho Quero dizer a linguagem Lua de copiar os métodos do módulo em variáveis ??locais no espaço o nome do módulo. Por exemplo:
-- 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
Se você fizer isso, as referências à io
módulos de ações e math
são feitas no momento em que require "somemodule"
é executado. Substituir um desses módulos após a chamada para require()
com uma versão zombou não será eficaz.
Para zombar efetivamente um módulo que é usado com este idioma, você teria que ter o objeto fictício no lugar antes a chamada para require()
.
Aqui é um como eu iria sobre a substituição do objeto io para a duração da chamada em um caso de teste:
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
I podem não ser restaurar o objeto real, exatamente no momento certo, e isso não foi testado, mas deve apontar na direção certa.