cppTUnit

minimalistic C++ unit testing framework

Projects

Once upon a time I was writing very simple file system for serial flash memory. Because testing and debugging tools on target platform (one quite old Renesas microcontroller) were pretty lame, I decided to write everything in Visual Studio first, write complete unit tests and then re-compile the whole thing with Embedded C++ compiler.

cppTUnit is tiny unit testing "framework" for Visual Studio I've written to do the testing part. Why I haven't used something really mature and proven like CPPUnit? Because it was easier to write my own stuff than spent several days by finding out how to integrate some full-fledged framework (the whole file system development took me like 10 days and was only small part of the whole project).

So, what can cppTUnit do? It is possible to write unit tests, organize them in fixtures and suites, run them and when something fails, go to the place where test failed and do some debugging and stuff. Predicates which compares results of tests are defined as templates, thus if there are requiremants on predicates parameters met (like overload of operator==() etc.), it is possible to compare results of any type.

To make thing easier, there are some concrete predicates implemented for most usual types (int, char, char* etc.). These concrete predicates are also able to format expected and actual values to Visual Studio's output window to give you some information about why test failed (see screesnshots).

How to use it? It is pretty simple. First of all it is necessary to add cppTUnit files to your project. These are:

  • CppTUnit.h - header with all imporant classes and macros declarations
  • CppTUnit.cpp - implementation of all important classes
  • Predicates.h - templates of predicates like AreEqual, IsTrue etc. (names are stolen from NUnit)
  • ConcretePredicates.h - some concrete predicates are declared in this header
  • ConcretePredicates.cpp - concrete predicates implementation

Once this is done, you can start write your tests.

Let's start with simple test fixture:

First, include CppTUnit.h:

#ifndef _SAMPLETESTFIXTURE_
#define _SAMPLETESTFIXTURE_

#include "CppTUnit.h"

Now declare a class, which must inherit from CppTUnit::TestFixture:

class SampleTestFixture : public CppTUnit::TestFixture {
public:

There must be a constructor, which initializes base class:

 SampleTestFixture() 
 : CppTUnit::TestFixture() {
 }

If needed, set-up and tear-down (for the whole fixture or for each test) methods can be overloaded:

 virtual void FixtureSetUp() {
 }
 virtual void FixtureTearDown() {
 }
 virtual void TestSetUp() {
 }
 virtual void TestTearDown() {
 }

Next step is to define test flow. For that reason there is a macro, which converts test-fixture methods to member functions pointers. That says, that all tests must be test fixture's member functions.

There is an optional parameter in AddToFlow() method, which says if particular test is enabled or disabled. Default value is true, or enabled.

 virtual void SetTestFlow() {
 SetName("My test fixture");
 AddToFlow(TEST(SampleTestFixture::Test1));
 AddToFlow(TEST(SampleTestFixture::Test2), true);
 AddToFlow(TEST(SampleTestFixture::Test3), false);
 AddToFlow(TEST(SampleTestFixture::Test4));
 }

Now we can write a simple test:

 void Test1() {
 int *p1 = new int();
 int *p2 = new int();
 ASSERT(CppTUnit::AreNotSame(*p1, *p2))
 ASSERT(CppTUnit::AreNotEqual(p1, p2))
 delete p1;
 p1 = p2;
 ASSERT(CppTUnit::AreSame(*p1, *p2))
 ASSERT(CppTUnit::AreEqual(p1, p2))
 delete p2;
 }

And second one:

 void Test2() {
 ASSERT(CppTUnit::AreEqual(1, 1))
 }

And third one (which will be skipped):

 void Test3() {
 ASSERT(CppTUnit::IsTrue(1 == 2))
 }

And finally fourth one, which should fail:

 void Test4() {
 ASSERT(CppTUnit::IsFalse(2 == 2))
 }
private:
};

Next step is to create test suite. Again it's necessary to inherit from CppTUnit::TestSuite class:

class SampleTestSuite : public CppTUnit::TestSuite {
public:

Test fixtures included in test suite are added in oveloaded method SetSuiteFlow():

 virtual void SetSuiteFlow() {
 SetName("My test suite");
 Add(&_fixture1);
 }

Of course test fixture must be instantiated in test suite class:

private:
 SampleTestFixture _fixture1;
};

Now we have our test fixture and test suite ready to be runned.

Let's create main() function:

int main() {

First we have to set on-fail behaviour. That is done by a static method SetOnFail() in test runner. It is possible to ignore failing tests and let the runner go to the end of all testsuites, or testing can be stopped on first failing test, or testing can be breaked on failing assert, and then you can check what's wrong.

We will use break here:

 CppTUnit::TestRunner::SetOnFail(CppTUnit::TestRunner::Break);

We need instance of our test suite:

 SampleTestFixture test_fixture;

Instance of test runner is also necessary:

 CppTUnit::TestRunner runner;

Finally, test runner can run test suite:

 runner.Run(test_fixture);
 return 0;
}

And that's it.

When this program is run, test runner will print test progress information in Visual Studio's output window:

------ Test fixture: 'My test fixture' ------
d:\mytemp\cpptunit\cpptunit\sampletestfixture.h(50): error
	Predicate 'IsFalse' failed: 
	Expected
		false
	but was
		true
cpptUnit.exe has triggered a breakpoint

We set Test4 to fail and fail behaviour to break, so Visual Studio will break on failing assert and ask if program should be breaked or continued:

 

First line of predicate-failed message is formatted the way Visual Studio can use to bring you to the right place in your source code (just double-click it).

Well, if you find this useful, download cppTUnit and try to play with it. Most of important things is implemented in CppTUnit.h, and there are also some comments (which can be extracted by Doxygen to make a very brief documentation).

Download

Download source and sample code.