Version 12 (modified by sam, 12 years ago) (diff)

layout

LolUnit tutorial

This tutorial is a quick introduction to LolUnit.

LolUnit is still in its early development stage. Feel free to send comments, suggestions or questions to sam@hocevar.net.

Getting started

Only one line is required to start using LolUnit:

#include <lol/unit.h>

No library and no compiler or linker flags are required as long as the <lol/unit.h> file is copied in a place where the compiler can find it, for instance in /usr/include/lol/unit.h.

Fixtures and tests

A fixture is a container class for tests that allows optional setup and teardown methods. To declare and automatically register a fixture, use the LOLUNIT_FIXTURE macro:

LOLUNIT_FIXTURE(MyFirstFixture)
{
};

A test is a method within a fixture that runs a series of checks. To declare and automatically register a test within a fixture, use the LOLUNIT_TEST macro:

LOLUNIT_FIXTURE(MyFirstFixture)
{
    LOLUNIT_TEST(MyFirstTest)
    {
    }
};

Add checks

Several macros are provided to perform various checks:

  • LOLUNIT_ASSERT(condition) will cause the current test to fail if condition is not met.
  • LOLUNIT_ASSERT_EQUAL(a, b) will cause the current test to fail if a and b are not equal.
  • LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, t) will cause the current test to fail if the floating point values a and b are not equal, with a tolerance of t.
  • all other comparison checks and their negations are available: LOLUNIT_ASSERT_GREATER, LOLUNIT_ASSERT_NOT_GREATER, etc.

This is how to populate our fixture:

LOLUNIT_FIXTURE(MyFirstFixture)
{
    LOLUNIT_TEST(Addition)
    {
        LOLUNIT_ASSERT_EQUAL(1 + 1, 2);
        LOLUNIT_ASSERT_LESS(2 + 2, 5); /* Check that our value for 2 is not too large */
        LOLUNIT_ASSERT_NOT_GEQUAL(2 + 2, 5);
    }

    LOLUNIT_TEST(Division)
    {
        double a = 1.0 / 2;
        double b = 1.0 / 3;
        double c = 1.0 / 6;
        LOLUNIT_ASSERT_DOUBLES_EQUAL(a - b, c, 1e-10);
    }
};

Run the test

Fixtures and their tests are automatically registered in a list global to the program. A TextTestRunner object can then be used to run all tests:

int main(void)
{
    lol::TextTestRunner runner;
    return !runner.run();
}

And this is the output:

..

OK (2 tests)

Test failures

Let's add yet another test to MyFirstFixture:

    LOLUNIT_TEST(ExpectedFailure)
    {
        double a = 0.1;
        double b = 0.2;
        LOLUNIT_ASSERT_DOUBLES_EQUAL(a, b, 1e-10);
    }

And running it:

...F

!!!FAILURES!!!
Test Results:
Run:  3  Failures: 1  Errors: 0

1) test: MyFirstFixture::ExpectedFailure (F) line: 23 tutorial.cpp
double equality assertion failed
- Expected: a = 0.1
- Actual  : b = 0.2
- Delta   : 1e-010

The test failure is properly spotted and information is given on where and how it happened.

Comparison with CppUnit

LolUnit is very similar to CppUnit, except it requires significantly less user input. This is what we would have done with CppUnit:

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestCase.h>
#include <cppunit/TestSuite.h>
#include <cppunit/TextTestRunner.h>

class MyFirstFixture : public CppUnit::TestCase
{
    CPPUNIT_TEST_SUITE(MyFirstFixture);
        CPPUNIT_TEST(Addition);
        CPPUNIT_TEST(Division);
        CPPUNIT_TEST(ExpectedFailure);
    CPPUNIT_TEST_SUITE_END();

    void Addition()
    {
        CPPUNIT_ASSERT_EQUAL(1 + 1, 2);
        CPPUNIT_ASSERT(2 + 2 < 5); /* Check that our value for 2 is not too large */
        CPPUNIT_ASSERT(!(2 + 2 >= 5));
    }

    void Division()
    {
        double a = 1.0 / 2;
        double b = 1.0 / 3;
        double c = 1.0 / 6;
        CPPUNIT_ASSERT_DOUBLES_EQUAL(a - b, c, 1e-10);
    }

    void ExpectedFailure()
    {
        double a = 0.1;
        double b = 0.2;
        CPPUNIT_ASSERT_DOUBLES_EQUAL(a, b, 1e-10);
    }
};
CPPUNIT_TEST_SUITE_REGISTRATION(MyFirstFixture);

int main(void)
{
    CppUnit::TextTestRunner runner;
    runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
    return !runner.run();
}

One major problem I have with this system is that Addition needs to be declared and defined at two different places. Similarly, MyFirstFixture needs to be declared, then given as an argument to CPPUNIT_TEST_SUITE, and finally as an argument to CPPUNIT_TEST_SUITE_REGISTRATION. There is no reason the user should have to duplicate this information.

And this is the output:

...F

!!!FAILURES!!!
Test Results:
Run:  3   Failures: 1   Errors: 0

1) test: MyFirstFixture::ExpectedFailure (F) line: 34 tutorial.cpp
double equality assertion failed
- Expected: 0.1
- Actual  : 0.2
- Delta   : 1e-10

Also, there is no way to compare two values and have them printed in case of failure. The equivalent of LOLUNIT_ASSERT_LESS(a, b), which will print the values of a and b in case of failure, is the less interesting CPPUNIT_ASSERT(a < b), which will only output true or false.