Question

Je suis un novice Lua. Je suis en train de tester le code Lua 5.1 à l'aide de Lunity . et LeMock .

Ma classe est StorageManager. Je suis en train de tester sa méthode load (), qui charge les fichiers à partir du disque. Je ne veux pas que mes tests unitaires dépendent des fichiers réels sur le disque pour le tester.

J'essaie donc d'injecter la dépendance d'E / S avec un objet fictif et de vérifier le comportement de cet objet lors de mon test. Je n'arrive pas à comprendre comment utiliser l'objet d'E / S avec une syntaxe qui fonctionne lorsqu'il est appelé à la fois par mon test unitaire fictif et par le "code réel".

Comment puis-je changer le code (méthode load (), de préférence) pour qu'il remplisse son rôle lorsqu'il est appelé à partir de l'un des tests unitaires (celui sans maquette est temporaire jusqu'à ce que je sache ce qu'il en est - il ressemble au code qui appellera plus tard la méthode testée)?

Remarque 1: Si vous exécutez ces tests, rappelez-vous que l'option "sans maquette". test attend un fichier sur le disque dont le nom de fichier correspond à VALID_FILENAME.

Note2: J'ai envisagé d'utiliser le comportement try / catch de pcall pour exécuter une ligne ou l'autre (voir storageManager.lua lignes 11 et 12). En supposant que ce soit même possible, cela ressemble à un hack, et cela piège les erreurs que je pourrais éventuellement vouloir jeter plus tard (out of load ()). Veuillez expliquer cette option si vous ne voyez aucune 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

Modifier:

Ces classes passent les deux tests. Un grand merci au 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
Était-ce utile?

La solution

Je pense que vous rendez le problème plus difficile qu'il ne le devrait.

En supposant que le module storageManager.lua ne localise pas lui-même le module io , il vous suffit alors de remplacer le io global par votre objet fictif pendant l'exécution. le test.

Si le module localise l'objet io pour améliorer les performances, vous devez alors injecter la nouvelle valeur de io avant de charger le module. Cela peut vouloir dire que vous devez appeler require dans la configuration du scénario de test (et un nettoyage correspondant qui supprime toutes les traces du module de package.loaded et < code> _G ) afin qu'il puisse être simulé différemment dans différents cas de test. WinImage

Modifier:

En localisant un module pour la performance, j'entends le langage Lua qui consiste à copier les méthodes du module dans des variables locales dans l'espace de noms du module. Par exemple:

-- 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

Si vous faites cela, les références aux modules de stock io et math sont faites au moment où nécessite que "somemodule" soit réalisé. Remplacer l’un de ces modules après l’appel de require () par une version simulée ne sera pas effectif.

Pour simuler efficacement un module utilisé avec cet idiome, vous devez placer l'objet factice à la place avant l'appel de require () .

Voici comment procéder pour remplacer l'objet io pendant la durée de l'appel dans un scénario de 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

Je ne restaure peut-être pas l'objet réel au bon moment, et cela n'a pas été testé, mais il devrait vous indiquer la bonne direction.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top