单元测试

lua-users home
wiki

单元测试是在开发过程中测试代码,而不是在生产环境中。通常,您首先准备测试环境,编写一些调用生产代码的代码,并检查预期结果与实际结果是否一致。

Lua 有几个框架可以做到这一点

它们的功能集基本相同,包括:

商业 Lua 测试工具可从 Software Verification 获取[2]

比较:[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 调用来测试模块 API,并使用 assert() 调用来验证结果。在这些模块中,使用 print() 输出有关测试进度的信息,并使用 Lua 注释来告知阅读源代码的人正在测试什么,也是常见的做法。尽管这种方法很普遍,但对于那些想要测试多个模块或想要更详细地查看结果的人来说,它可能过于粗糙。

Shake 假设测试已经为上述场景实现了,但它提供了一个透明的测试引擎,供那些想要以批处理模式执行测试或想要获得各种结果概述的人使用。Shake 引擎不仅可以告知一组测试脚本中发现的测试、失败和错误的数量,还可以使用与 assert() 调用相关的输出和注释来推断有关测试上下文的信息。

Shake 的主要特点是透明性,这意味着模块作者和测试编写者不需要知道测试将使用 Shake 运行。只要测试调用 assert(),Shake 就可以从源代码和运行时执行中获取大量信息。这是通过使用 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 日下午 4:19 GMT (差异)