Writing and Running CUTE Unit Test Suites

Here you will learn how to create and run tests for your code using the CUTE C++ unit testing framework. We begin with the initial trivial test src/Test.cpp that is created by the Using the CUTE Eclipse Plug-in.

Source File Organization

Before you start writing tests, you need a plan for organizing your source files.

Single File

If your test is short enough to fit into one file, then you can simply add it to the trivial source file src/Test.cpp provided by CUTE:

#include "cute.h" 
#include "ide_listener.h" 
#include "xml_listener.h" 
#include "cute_runner.h" 

// TODO #include the headers for the code you want to test

// TODO Add your test functions

void thisIsATest() {
    ASSERTM("start writing tests", false);    
}

bool runAllTests(int argc, char const *argv[]) {
    cute::suite s { };

    //TODO add your test here

    s.push_back(CUTE(thisIsATest));
    cute::xml_file_opener xmlfile(argc, argv);
    cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
    auto runner = cute::makeRunner(lis, argc, argv);
    bool success = runner(s, "AllTests");
    return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}
Edit this file:
  1. #include the header files for the classes you are testing.
  2. Replace function thisIsATest() with your test functions.
  3. Replace thisIsATest in s.push_back(CUTE(thisIsATest)) with your test functions.

Partitioning Into Multiple Files

Chances are, you will want to partition your tests into multiple files. Generally, it is best to have one test suite for each source file in the project that you are unit testing. The test suite consists of a header (.h) file and an implementation (.cpp) file. Name them consistently. For example, put class myclass in files myclass.cpp and myclass.h, and put the unit test for myclass in myclassTest.cpp and myclassTest.h.

Writing Tests

Code Your Tests Using CUTE Assertions

The test consists of a series of lines that set up some situation to be checked, followed by a CUTE assertion to perform the check.

In your test implementation file (myclassTest.cpp in the above example), include the file that defines the CUTE assertions:

#include "cute.h" 

The header cute.h provides a variety of macros you can use to verify conditions. Most assertions have two versions: one version uses the source code of the test itself as the message, and the other allows you to specify your own message msg.

ASSERTM(msg, cond)
ASSERT(cond)

If cond is false, the test fails.

FAILM(msg)
FAIL()

Fail unconditionally. The message "FAIL()" is used if no message is specified.

ASSERT_EQUALM(msg, expected, actual)
ASSERT_EQUAL(expected, actual)

If expected and actual are not equal, fail and print the values of expected and actual. Specify an unsigned constant when comparing to unsigned value. For example,

ASSERT_EQUAL(5u, vect.size());

Take care to specify the expected value followed by the actual value, as shown above. If you reverse them, they appear backwards in the failure message.

ASSERT_NOT_EQUAL_TOM(msg, left, right)
ASSERT_NOT_EQUAL_TO(left, right)

Fail if left and right are equals.

ASSERT_EQUAL_DELTAM(msg, expected, actual, delta)
ASSERT_EQUAL_DELTA(expected, actual, delta)

Fail if expected and actual are different by more than delta. Use this assertion for real numbers.

ASSERT_EQUAL_RANGESM(msg, expbeg, expend, actbeg, actend)
ASSERT_EQUAL_RANGES(expbeg, expend, actbeg, actend)

Fail if the ranges defined by expbeg and expend, and actbeg and actend are different.

ASSERT_THROWSM(msg, code, exception)
ASSERT_THROWS(code, exception)

Fail if code does not throw exception of type exception.

void test_that_something_throws() {
    ASSERT_THROWS(should_throw_std_exception(),std::exception);
}

ASSERT_GREATERM(msg, left, right)
ASSERT_GREATER(left, right);
ASSERT_GREATER_EQUALM(msg, left, right)
ASSERT_GREATER_EQUAL(left, right);
ASSERT_LESSM(msg, left, right)
ASSERT_LESS(left, right);
ASSERT_LESS_EQUALM(msg, left, right)
ASSERT_LESS_EQUAL(left, right);

Fail if left is greater/greater equals/lesser/lesser equals than right.

ASSERT*_DDTM(msg, cond, failure)
ASSERT*_DDT(cond, failure)

All the above macros are available with _DDT in the macro name. Use these macros to do Data Driven Testing.

Put these assertions in the test implementation file (myclassTest.cpp in the above example).

Collect the Tests In a Test Suite

A CUTE test suite is a vector of tests. The tests are executed in the order in which they were appended to the suite. If an assertion in some test fails, the failure is reported, and the rest of the test is skipped. Execution continues with the next test in the suite. This means that a suite of many short tests is better than a few long tests:

  1. With shorter tests, less test code is skipped upon a failure.
  2. Each test can fail at most once, so a suite with more tests will show more failures to help you pinpoint bugs.

In the trivial source file provided with CUTE src/Test.cpp, include the test header file for your test. For example,

#include "myclassTest.h" 

When the Test Is a Simple Function

If you prefer to write your tests as simple functions, implement the test function, and push it on the test suite using the CUTE() macro:

s.push_back(CUTE(mytestfunction));

When the Test Is a Functor

If you prefer to implement your test as a class or struct, define a functor class in a header file, say myclassTest.h:

// File myclassTest.h

class myclassTest {
public:
    myclassTest();
    // Must define void operator() with no arguments.
    // In implementation: add calls to cute-assert functions and methods like someFunction1
    void operator()();

private:
    // Whatever methods you need
    void someFunction1();
    void someFunction2();

    // Whatever member variables you need
    int memberVar1;
    int memberVar2;
};

Put the implementation of mytestClass in a separate file, like myclassTest.cpp.

Returning to the test suite code (src/Test.cpp), include the test class header file and add the test functor to the test suite:

#include "cute.h" 
#include "ide_listener.h" 
#include "xml_listener.h" 
#include "cute_runner.h" 

// TODO #include the headers for the code you want to test
#include "myclassTest.h" 
#include "anotherclassTest.h" 

bool runAllTests(int argc, char const *argv[]) {
    cute::suite s { };

    //TODO add your test here
    s.push_back(myclassTest{ });
    s.push_back(anotherclassTest{ });

    cute::xml_file_opener xmlfile(argc, argv);
    cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
    auto runner = cute::makeRunner(lis, argc, argv);
    bool success = runner(s, "AllTests");
    return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Running the CUTE Test

Compile and execute the test. The tests will be executed in the order in which they were appended to the suite. If an assertion fails, it is reported through the listener, and the test containing the failed assertion is aborted. Execution continues with the next test in the suite.

Further Readings