Lua I / O Dependency Injection
-
06-07-2019 - |
Frage
Ich bin ein Anfänger Lua. Ich bin Unit-Tests Lua 5.1 Code LUNITY und LeMock .
Meine Klasse ist Storagemanager. Ich bin Einheit ihre Last () Methode zu testen, welche Dateien von der Festplatte geladen wird. Ich will nicht meine Einheit auf tatsächliche Dateien auf dem tatsächlichen Disk abhängig prüft, ob zu testen.
Also, ich versuche, die I / O-Abhängigkeit mit einem Mock-Objekt zu injizieren und dieses Objekts Verhalten während meines Tests zu überprüfen. Ich kann nicht herausfinden, wie die I / O-Objekt mit einer Syntax zu verwenden, die, wenn sie durch meine beiden Mock-basierten Gerät zu testen und den „echten Code“.
genannt funktioniertWie kann ich den Code (die Last () -Methode, vorzugsweise) ändern, so dass sie ihre Arbeit tun, wenn entweder von Unit-Test (das eine ohne das Mock genannt ist vorübergehend, bis ich das herausgefunden bekommen - es ist der ähnelt Code, der später tatsächlich die Methode im Test)?
rufenHinweis 1:. Wenn Sie diese Tests durchführen, denken Sie daran, dass die „ohne Mock“ Test eine Datei auf der Festplatte, deren Dateinamen erwartet einstimmt VALID_FILENAME
Hinweis 2: Ich dachte über pcall der try / catch-ähnliches Verhalten mit einer Leitung oder die andere (siehe storageManager.lua Linien 11 und 12) auszuführen. Unter der Annahme, dass es sogar möglich ist, fühlt es sich wie ein Hack, und es fängt Fehler könnte ich später geworfen werden soll (außer Last ()). Bitte erläutern Sie diese Option, wenn Sie keine andere Möglichkeit sehen.
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:
passieren Diese Klassen beiden Tests. Vielen Dank an die 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
Lösung
Ich denke, dass Sie das Problem machen schwieriger, als es sein muss.
Unter der Annahme, dass das Modul storageManager.lua nicht selbst io
Modul lokalisierende, dann alles, was Sie tun müssen, ist die globale io
mit Ihrem Mock-Objekt zu ersetzen, während der Test läuft.
Wenn das Modul das io
Objekt für die Leistung tut lokalisieren, dann müßten Sie den neuen Wert von io
injizieren, bevor das Modul geladen werden. Dies könnte bedeuten, dass Sie den Anruf tätigen müssen Teil des Testfall-Setup require
(und eine passende Bereinigung, die alle Spuren des Moduls von package.loaded
und _G
entfernt), so dass es unterschiedlich in verschiedenen Testfällen verspottet werden kann.
WinImage
Edit:
Durch ein Modul für Performance lokalisierende meine ich das Lua Idiom des Kopierens der Methoden des Moduls in lokale Variablen im Namensraum des Moduls. Zum Beispiel:
-- 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
Wenn Sie dies tun, werden die Verweise auf die Lager Module io
und math
sind zur Zeit gemacht, dass require "somemodule"
ausgeführt wird. Ersetzen eine dieser beiden Module nach dem Aufruf mit einer verspottet Version require()
wird nicht wirksam sein.
effektiv Um ein Modul zu verspotten, die mit diesem Idiom verwendet wird, würden Sie das Mock-Objekt an der richtigen Stelle müssen vor der Aufruf von require()
.
Hier ist eine, wie ich gehen würde, das io-Objekt für die Dauer des Anrufs in einem Testfall zu ersetzen:
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
Ich kann nicht das reale Objekt werden die Wiederherstellung genau im richtigen Moment, und das ist nicht getestet, aber es sollte Sie in die richtige Richtung zeigen.