2009-06-20 1 views
0

私はLuaの初心者です。私はLunityLeMockを使用してユニットテストのLua 5.1のコードです。LuaのI/Oの依存性注入

私のクラスは、StorageManagerです。私は、ディスクからファイルを読み込むload()メソッドを単体テストしています。私は単体テストを実際のディスク上の実際のファイルに依存させてテストすることは望ましくありません。

だから、私はモックオブジェクトとI/O依存性を注入し、私のテスト中に、そのオブジェクトの動作を検証しようとしています。私は私のモックベースのユニットテストと「実際のコード」の両方に呼び出されたときに動作します構文でI/Oオブジェクトを使用する方法を見つけ出すことはできません。

単体テストから呼び出されたときに仕事をするようにコードを変更するにはどうすればよいですか(load()メソッドが好ましい)。あとで実際にテスト中のメソッドを呼び出すコード)?

注1:あなたはこれらのテストを実行した場合、テスト「モックなし」のファイル名VALID_FILENAMEと一致するディスク上のファイルを期待していることを覚えておいてください。

注2:pcallのtry/catchのような動作を使用して、1行または別の行を実行することを考えました(storageManager.lua行11 & 12を参照)。それが可能であっても、それはハックのように感じられ、後で投げたいかもしれないエラーをトラップする(out of load())。代替手段が見つからない場合は、このオプションで解説してください。

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 

編集:

これらのクラスは、両方の試験に合格。 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 

答えて

1

私はあなたがそれがなければならないよりも、問題をより困難にしていると思います。

モジュールstorageManager.lua自体がioモジュールをローカライズしていないと仮定すると、テスト実行中にグローバルioをモックオブジェクトに置き換えるだけです。

モジュールがioオブジェクトのパフォーマンスをローカライズする場合は、モジュールをロードする前に新しい値のioを注入する必要があります。これは、テストケースの設定の一部(package.loaded_Gからモジュールのすべてのトレースを削除するマッチングクリーンアップ)の一部をrequireに呼び出す必要があることを意味します。 WinImage

編集:

パフォーマンスのためのモジュールをローカライズすることにより、私は、モジュールの名前空間内のローカル変数にモジュールのメソッドをコピーするの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 

は、ストック・モジュールiomathへの参照がrequire "somemodule"が実行された時点で行われます。 require()への呼び出しの後で、これらのモジュールのいずれかを模倣したバージョンに置き換えることは有効ではありません。効果的にこのイディオムで使用されるモジュールを模擬するために

、あなたはrequire()への呼び出し前に行わにモックオブジェクトを持っている必要があります。ここで

は、私がテストケースにはコールの期間IOオブジェクトを置き換えることについては行くだろうかです:

 

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 

私は正確に適切なタイミングで実際のオブジェクトを復元し、このことはできませんテストされていませんが、正しい方向に向けるべきです。

+0

「パフォーマンスのためにioオブジェクトをローカライズする」ことを意味するわけではありませんが、「テスト中にグローバルioを[my] mockオブジェクトに置き換えたい」とします。私はstorageManager.luaでそれをやっていると思った:4?どのようにして「グローバルio」を適切に置き換えるのでしょうか?あなたはサンプルコードを提供できますか? – lance

+0

あなたの編集は私に必要なものを与えました。変更されたコードを元の質問に編集しました。どうもありがとう。 – lance