單元測試

lua-users home
wiki

單元測試是關於在開發過程中測試您的程式碼,而不是在生產環境中。通常,您從準備測試環境開始,撰寫一些呼叫生產程式碼的程式碼,並檢查預期結果和實際結果。

Lua 有幾個框架可以做到這一點

它們大多具有相同的特性,即

Software Verification [2] 提供商業化的 Lua 測試工具。

比較:[3]

Lua 本身的測試(適用於測試 LuaImplementations

luaunit

由於本人比較熟悉 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

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]: ?

另請參閱


最近變更 · 喜好設定
編輯 · 記錄
上次編輯時間為 2017 年 4 月 22 日, GMT 4:19 下午 (相異)