Seconda iniezione di dipendenza I / O
-
06-07-2019 - |
Domanda
Sono una novizia di Lua. Sto testando il codice Lua 5.1 utilizzando Lunity e LeMock .
La mia classe è StorageManager. Sto testando il suo metodo load (), che carica i file dal disco. Non voglio che i miei test unitari dipendano dai file effettivi sul disco effettivo per testarlo.
Quindi, sto cercando di iniettare la dipendenza I / O con un oggetto simulato e verificare il comportamento di quell'oggetto durante il mio test. Non riesco a capire come usare l'oggetto I / O con una sintassi che funziona quando viene chiamato sia dal mio test unit basato su mock sia dal "codice reale".
Come posso cambiare il codice (il metodo load (), preferibilmente) in modo che possa fare il suo lavoro quando viene chiamato da uno dei test unitari (quello senza la simulazione è temporaneo fino a quando non lo capisco - assomiglia al codice che in seguito chiamerà effettivamente il metodo sotto test)?
Nota1: se esegui questi test, ricorda che il " senza finto " test prevede un file su disco il cui nome file corrisponde a VALID_FILENAME.
Nota2: ho pensato di usare il comportamento try / catch-like di pcall per eseguire una riga o l'altra (vedi storageManager.lua righe 11 & amp; 12). Supponendo che sia persino possibile, sembra un trucco e intrappola gli errori che potrei voler lanciare in seguito (senza carico ()). Spiega questa opzione se non vedi alternative.
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
Modifica
Queste classi superano entrambi i test. Mille grazie a 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
Soluzione
Penso che tu stia rendendo il problema più difficile di quanto non debba essere.
Supponendo che il modulo storageManager.lua non stia localizzando il modulo io
, quindi tutto ciò che devi fare è sostituire il io
globale con il tuo oggetto simulato mentre sei in esecuzione il test.
Se il modulo localizza l'oggetto io
per le prestazioni, è necessario iniettare il nuovo valore di io
prima di caricare il modulo. Ciò potrebbe significare che è necessario effettuare la chiamata a richiedi
parte dell'installazione del test case (e una pulizia corrispondente che rimuove tutte le tracce del modulo da package.loaded
e < code> _G ) in modo che possa essere deriso in modo diverso in diversi casi di test.
WinImage
Modifica
Localizzando un modulo per prestazioni intendo il linguaggio Lua di copiare i metodi del modulo in variabili locali nello spazio dei nomi del modulo. Ad esempio:
-- 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
In questo caso, i riferimenti ai moduli stock io
e math
vengono fatti nel momento in cui richiede " somemodule & ;> è eseguito. La sostituzione di uno di questi moduli dopo la chiamata a
richiedono ()
con una versione derisa non sarà efficace.
Per deridere efficacemente un modulo utilizzato con questo idioma, dovresti avere l'oggetto finto in posizione prima la chiamata a richiedono ()
.
Ecco come farei per sostituire l'oggetto io per la durata della chiamata in un caso di test:
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
Potrei non ripristinare l'oggetto reale esattamente nel momento giusto, e questo non è testato, ma dovrebbe indirizzarti nella giusta direzione.