單元測試 |
|
Lua 有幾個框架可以做到這一點
它們大多具有相同的特性,即
Software Verification [2] 提供商業化的 Lua 測試工具。
比較:[3]
Lua 本身的測試(適用於測試 LuaImplementations)
由於本人比較熟悉 luaunit,接下來將提供一個 luaunit 的範例。範例非常容易理解。
-- Some super function to test function my_super_function( arg1, arg2 ) return arg1 + arg2 end -- Unit testing starts require('luaunit') TestMyStuff = {} --class function TestMyStuff:testWithNumbers() a = 1 b = 2 result = my_super_function( a, b ) assertEquals( type(result), 'number' ) assertEquals( result, 3 ) end function TestMyStuff:testWithRealNumbers() a = 1.1 b = 2.2 result = my_super_function( a, b ) assertEquals( type(result), 'number' ) -- I would like the result to be always rounded to an integer -- but it won't work with my simple implementation -- thus, the test will fail assertEquals( result, 3 ) end -- class TestMyStuff LuaUnit:run()
執行範例後,你會得到
shell $ lua use_luaunit1.lua Started on 12/20/14 14:37:50 >>>>>>>>> TestMyStuff >>> TestMyStuff.testWithNumbers >>> TestMyStuff.testWithRealNumbers test.lua:24: expected: 3, actual: 3.3 Failed ========================================================= Failed tests: ------------- >>> TestMyStuff.testWithRealNumbers failed test.lua:24: expected: 3, actual: 3.3 Success: 50% - 1 / 2, executed in 0.015 seconds
測試架構會回報導致錯誤的檔案和行,以及一些額外資訊。
隨著程式成長,你會需要更多測試案例。下一個範例稍微複雜一些。
-- Some super function to test function my_super_function( arg1, arg2 ) return arg1 + arg2 end function my_bad_function( arg1, arg2 ) return arg1 - arg2 end -- Unit testing starts require('luaunit') -- now, we perform all the tests on int in one test class -- and the tests on float in another one -- when your test grows, you will have many test classes TestWithInt = {} --class function TestWithInt:setUp() -- this function is run before each test, so that multiple -- tests can share initialisations self.a = 1 self.b = 2 end function TestWithInt:tearDown() -- this function is executed after each test -- here, we have nothing to do so we could have avoid -- declaring it end function TestWithInt:testSuperFunction() result = my_super_function( self.a, self.b ) assertEquals( type(result), 'number' ) assertEquals( result, 3 ) end function TestWithInt:testBadFunction() result = my_bad_function( self.a, self.b ) assertEquals( type(result), 'number' ) assertEquals( result, -1 ) end function TestWithInt:testThatFails() -- you can test anything with assertEquals assertEquals( self.a, 1 ) assertEquals( type(self.a), 'number' ) -- will fail assertEquals( 'hop', 'bof' ) end -- class TestWithInt TestWithFloat = {} --class function TestWithFloat:setUp() -- this function is run before each test, so that multiple -- tests can share initialisations self.a = 1.1 self.b = 2.1 end function TestWithFloat:tearDown() -- this function is executed after each test -- here, we have nothing to do so we could have avoid -- declaring it end function TestWithFloat:testSuperFunction() result = my_super_function( self.a, self.b ) assertEquals( type(result), 'number' ) -- will fail assertEquals( result, 3 ) end function TestWithFloat:testBadFunction() result = my_bad_function( self.a, self.b ) assertEquals( type(result), 'number' ) -- will work, but only by chance :-) assertEquals( result, -1 ) end -- class TestWithFloat LuaUnit:run()
執行範例
shell $ lua use_luaunit2.lua >>>>>> TestWithFloat >>> TestWithFloat:testSuperFunction use_luaunit2.lua:66: expected: 3.2, actual: 3 Failed >>> TestWithFloat:testBadFunction Ok >>>>>> TestWithInt >>> TestWithInt:testSuperFunction Ok >>> TestWithInt:testBadFunction Ok >>> TestWithInt:testThatFails use_luaunit2.lua:44: expected: 'hop', actual: 'bof' Failed Success : 60% - 3 / 5
你也可以個別執行測試
shell $ lua use_luaunit2.lua TestWithInt:testSuperFunction >>>>>> TestWithInt >>> TestWithInt:testSuperFunction Ok Success : 100% - 1 / 1 shell $ lua use_luaunit2.lua TestWithFloat >>>>>> TestWithFloat >>> TestWithFloat:testSuperFunction use_luaunit2.lua:66: expected: 3.2, actual: 3 Failed >>> TestWithFloat:testBadFunction Ok Success : 50% - 1 / 2
Shake 是一個簡單透明的 Lua 測試引擎,它假設測試只使用標準的 assert 及 print 呼叫。如果你正在尋找 xUnit 風格的框架,請查看 lunit 和 luaunit。
許多 Lua 模組和應用程式在執行內部測試時,都使用一種簡單的模式。它們有一個指令碼(通常稱為 test.lua),使用 API 呼叫和 assert() 呼叫來測試模組 API,並驗證結果。這些模組中常見的做法是使用 print() 輸出測試進度的資訊,並使用 Lua 註解告知讀取程式碼的人員正在測試什麼。儘管此方法廣泛使用,但對於那些想要一次測試多個模組或想要更詳細檢視結果的人來說,可能太過粗糙。
Shake 假設已針對上述情況實作測試,但為那些希望以批次模式執行測試或希望瞭解各種結果的人提供一個透明的測試引擎。Shake 引擎不僅可以告知在群組中找到的測試、失敗和錯誤數目,還可以利用與 assert() 呼叫相關的輸出和註解來推論有關測試內容的資訊。
Shake 的主要特徵是透明性,這表示模組作者和測試撰寫人員不必知道測試將使用 Shake 執行。只要測試呼叫 assert(),Shake 就可以從來源和執行時間中獲得相當多的資訊。這是透過使用 Leg(以及 LPeg)預處理測試來源碼來完成,使用 Leg(以及 LPeg)將每個 assert() 呼叫替換為一個呼叫,可以從中擷取與斷言相關的表示式、值和運算子資訊。
假設你已經安裝了像是 LuaFileSystem 模組,你前往其 /tests 目錄並從那裡執行 Shake,輸出將會是
>>>>~/workspace/luafilesystem/tests$ shake -> test.lua OK! _________________ Tests: 27 Failures: 0 Errors: 0
另一方面,如果你有像下面這樣的測試指令碼,其中包含兩個應該會失敗的斷言(行數已編號)
1 items = 10 2 -- checks the correct case 3 assert (items == 10, "this should not fail") 4 5 items = 20 6 -- checks an overflow case 7 assert (items == 10, "wrong number of items") 8 9 print("Verifying the total") 10 items = 10 11 total = 30 12 assert (items == total, "wrong total")
Shake 將會記錄失敗,但會執行整個測試指令碼,最後會回報
:~/workspace$ shake ---------------- test.lua failed! ---------------- -- checks an overflow case #7 assert (items == 10, "wrong number of items") items -> 20 Verifying the total #12 assert (items == total, "wrong total") items -> 10 total -> 30 _________________ Tests: 3 Failures: 2 Errors: 0
請注意,與使用 Lua 執行測試指令碼的預設輸出相比,這種方式的資訊量要來得多
:~/workspace$ lua5.1 test.lua lua5.1: test.lua:7: wrong number of items stack traceback: [C]: in function 'assert' test.lua:7: in main chunk [C]: ?