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.