Getting started with LuaUnit
This section will guide you through a step by step usage of LuaUnit . The full source code of the example below is available in the : Annex C: Source code of example or in the file my_test_suite.lua in the doc directory.
Setting up your test script
To get started, create your file my_test_suite.lua .
The script should import LuaUnit:
lu = require('luaunit')
The last line executes your script with LuaUnit and exit with the proper error code:
os.exit( lu.LuaUnit.run() )
Now, run your file with:
lua my_test_suite.lua
It prints something like:
Ran 0 tests in 0.000 seconds, 0 successes, 0 failures
OK
Now, your testing framework is in place, you can start writing tests.
Writing tests
LuaUnit scans all variables that start with test or Test. If they are functions, or if they are tables that contain functions that start with test or Test, they are run as part of the test suite. Note that this prefix can be ajusted with command-line options (see Command-line options).
So just write a function whose name starts with test. Inside test functions, use the assertions functions provided by LuaUnit, such
as assertEquals().
Let’s see that in practice.
Suppose you want to test the following add function:
function add(v1,v2)
-- add positive numbers
-- return 0 if any of the numbers are 0
-- error if any of the two numbers are negative
if v1 < 0 or v2 < 0 then
error('Can only add positive or null numbers, received '..v1..' and '..v2)
end
if v1 == 0 or v2 == 0 then
return 0
end
return v1+v2
end
You write the following tests:
function testAddPositive()
lu.assertEquals(add(1,1),2)
end
function testAddZero()
lu.assertEquals(add(1,0),0)
lu.assertEquals(add(0,5),0)
lu.assertEquals(add(0,0),0)
end
assertEquals() is the most commonly used assertion function. It
verifies that both argument are equals, in the order actual value, expected value.
Rerun your test script (-v is to activate a more verbose output):
$ lua my_test_suite.lua -v
It now prints:
Started on 02/19/17 22:15:53
TestAdd.testAddPositive ... Ok
TestAdd.testAddZero ... Ok
=========================================================
Ran 2 tests in 0.003 seconds, 2 successes, 0 failures
OK
You always have:
the date at which the test suite was started
the group to which the function belongs (usually, the name of the function table, and <TestFunctions> for all direct test functions)
the name of the function being executed
a report at the end, with number of executed test, number of non selected tests if any, number of failures, number of errors (if any) and duration.
The difference between failures and errors are:
luaunit assertion functions generate failures
any unexpected error during execution generates an error
failures or errors during setup() or teardown() always generate errors
If we continue with our example, we also want to test that when the function receives negative numbers, it generates an error. Use
assertError() or even better, assertErrorMsgContains() to also validate the content
of the error message. There are other types or error checking functions, see Error assertions . Here
we use assertErrorMsgContains() . First argument is the expected message, then the function to call
and the optional arguments:
function testAddError()
lu.assertErrorMsgContains('Can only add positive or null numbers, received 2 and -3', add, 2, -3)
end
Now, suppose we also have the following function to test:
function adder(v)
-- return a function that adds v to its argument using add
function closure( x ) return x+v end
return closure
end
We want to test the type of the value returned by adder and its behavior. LuaUnit
provides assertion for type testing (see Type assertions ). In this case, we use
assertIsFunction():
function testAdder()
f = adder(3)
lu.assertIsFunction( f )
lu.assertEquals( f(2), 5 )
end
Grouping tests, setup/teardown functionality
When the number of tests starts to grow, you usually organise them into separate groups. You can do that with LuaUnit by putting them inside a table (whose name must start with Test or test ).
For example, assume we have a second function to test:
function div(v1,v2)
-- divide positive numbers
-- return 0 if any of the numbers are 0
-- error if any of the two numbers are negative
if v1 < 0 or v2 < 0 then
error('Can only divide positive or null numbers, received '..v1..' and '..v2)
end
if v1 == 0 or v2 == 0 then
return 0
end
return v1/v2
end
We move the tests related to the function add into their own table:
TestAdd = {}
function TestAdd:testAddPositive()
lu.assertEquals(add(1,1),2)
end
function TestAdd:testAddZero()
lu.assertEquals(add(1,0),0)
lu.assertEquals(add(0,5),0)
lu.assertEquals(add(0,0),0)
end
function TestAdd:testAddError()
lu.assertErrorMsgContains('Can only add positive or null numbers, received 2 and -3', add, 2, -3)
end
function TestAdd:testAdder()
f = adder(3)
lu.assertIsFunction( f )
lu.assertEquals( f(2), 5 )
end
-- end of table TestAdd
Then we create a second set of tests for div:
TestDiv = {}
function TestDiv:testDivPositive()
lu.assertEquals(div(4,2),2)
end
function TestDiv:testDivZero()
lu.assertEquals(div(4,0),0)
lu.assertEquals(div(0,5),0)
lu.assertEquals(div(0,0),0)
end
function TestDiv:testDivError()
lu.assertErrorMsgContains('Can only divide positive or null numbers, received 2 and -3', div, 2, -3)
end
-- end of table TestDiv
Execution of the test suite now looks like this:
Started on 02/19/17 22:15:53
TestAdd.testAddError ... Ok
TestAdd.testAddPositive ... Ok
TestAdd.testAddZero ... Ok
TestAdd.testAdder ... Ok
TestDiv.testDivError ... Ok
TestDiv.testDivPositive ... Ok
TestDiv.testDivZero ... Ok
=========================================================
Ran 7 tests in 0.006 seconds, 7 successes, 0 failures
OK
When tests are defined in tables, you can optionally define two special functions, setUp() and tearDown(), which will be executed respectively before and after every test.
These function may be used to create specific resources for the test being executed and cleanup the test environment.
For a practical example, imagine that we have a log() function that writes strings to a log file on disk. The file is created upon first usage of the function, and the filename is defined by calling the function initLog().
The tests for these functions would take advantage of the setup/teardown functionality to prepare a log filename shared by all tests, make sure that all tests start with a non existing log file name, and delete the log filename after every test:
TestLogger = {}
function TestLogger:setUp()
-- define the fname to use for logging
self.fname = 'mytmplog.log'
-- make sure the file does not already exists
os.remove(self.fname)
end
function TestLogger:testLoggerCreatesFile()
initLog(self.fname)
log('toto')
-- make sure that our log file was created
f = io.open(self.fname, 'r')
lu.assertNotNil( f )
f:close()
end
function TestLogger:tearDown()
-- cleanup our log file after all tests
os.remove(self.fname)
end
Note
Errors generated during execution of setUp() or tearDown() functions are considered test failures.
Note
For compatibility with luaunit v2 and other lua unit-test frameworks, setUp() and tearDown() may also be named setup(), SetUp(), Setup() and teardown(), TearDown(), Teardown().
Using the command-line
You can control the LuaUnit execution from the command-line:
Output format*
Choose the test output format with -o or --output. Available formats are:
text: the default output format
nil: no output at all
tap: TAP format
junit: output junit xml
Example of non-verbose text format:
$ lua doc/my_test_suite.lua
.......
Ran 7 tests in 0.003 seconds, 7 successes, 0 failures
OK
Example of TAP format:
$ lua doc/my_test_suite.lua -o TAP
1..7
# Started on 02/19/17 22:15:53
# Starting class: TestAdd
ok 1 TestAdd.testAddError
ok 2 TestAdd.testAddPositive
ok 3 TestAdd.testAddZero
ok 4 TestAdd.testAdder
# Starting class: TestDiv
ok 5 TestDiv.testDivError
ok 6 TestDiv.testDivPositive
ok 7 TestDiv.testDivZero
# Ran 7 tests in 0.007 seconds, 7 successes, 0 failures
Output formats may also be controlled by the following environment variables: * LUAUNIT_OUTPUT: output format to use * LUAUNIT_JUNIT_FNAME: for junit output format, name of the xml file
For a more detailed overview of all formats and their verbosity see the section Output formats .
List of tests to run
You can list some test names on the command-line to run only those tests. The name must be the exact match of either the test table, the test function or the test table and the test method. The option may be repeated.
Example:
-- Run all TestAdd table tests and one test of TestDiv table.
$ lua doc/my_test_suite.lua TestAdd TestDiv.testDivError -v
Started on 02/19/17 22:15:53
TestAdd.testAddError ... Ok
TestAdd.testAddPositive ... Ok
TestAdd.testAddZero ... Ok
TestAdd.testAdder ... Ok
TestDiv.testDivError ... Ok
=========================================================
Ran 5 tests in 0.003 seconds, 5 successes, 0 failures
OK
Including / excluding tests
The most flexible approach for selecting tests to use the include and exclude functionality.
With --pattern or -p, you can provide a lua pattern and only the tests that contain
the pattern will actually be run.
Example:
-- Run all tests of zero testing and error testing
-- by using the magic character .
$ lua my_test_suite.lua -v -p Err.r -p Z.ro
For our test suite, it gives the following output:
Started on 02/19/17 22:15:53
TestAdd.testAddError ... Ok
TestAdd.testAddZero ... Ok
TestDiv.testDivError ... Ok
TestDiv.testDivZero ... Ok
=========================================================
Ran 4 tests in 0.003 seconds, 4 successes, 0 failures, 3 non-selected
OK
The number of tests ignored by the selection is printed, along with the test result. The pattern can be any lua pattern. Be sure to exclude all magic characters with % (like -+?*) and protect your pattern from the shell interpretation by putting it in quotes.
You can also exclude tests that match some patterns:
Example:
-- Run all tests except zero testing and except error testing
$ lua my_test_suite.lua -v -x Error -x Zero
For our test suite, it gives the following output:
Started on 02/19/17 22:29:45
TestAdd.testAddPositive ... Ok
TestAdd.testAdder ... Ok
TestDiv.testDivPositive ... Ok
=========================================================
Ran 3 tests in 0.003 seconds, 3 successes, 0 failures, 4 non-selected
OK
You can also combine test selection and test exclusion. See Flexible test selection
Conclusion
You now know enough of LuaUnit to start writing your test suite. Check the reference documentation for a complete list of assertions, command-line options and specific behavior.