Unit Testing |
|
Lua has these frameworks to do that:
Both have mostly the main feature set, which is:
Commercial Lua testing tools are available from Software Verification[1].
I am going to provide an example for the second one (luaunit) since I am more familiar with it. The example is pretty simple to understand.
-- 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()
When you run it, you get:
shell $ lua use_luaunit1.lua >>>>>> TestMyStuff >>> TestMyStuff:testWithNumbers Ok >>> TestMyStuff:testWithRealNumbers use_luaunit1.lua:25: expected: 3.3, actual: 3 Failed Success : 50% - 1 / 2
The testing framework reports the file and line that caused the error, and some additional information.
When your program grows, you need more and more test cases. The next example is slightly more complicated.
-- 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()
Run it:
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
You can also run tests individually:
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