Today, while writing some unit tests, I encountered a challenge. The user story was that, when a Person’s details are updated, the display should be updated to reflect the changes.

I’d implemented this feature using a signal on the person class that will be called whenever any details are updated:

class person
{
public:
	// Set the Person's name.
	void name(const std::string & name)
	{
		name_ = name;
		updated(*this);
	};

	// An signal that will be called when the person's details are updated.
	boost::signal<void(const person & person)> updated;

private:
	// The person's name.
	std::string name_;
};

This is a fairly standard application of an observer pattern that you might find in any MVC application.

But the question is, using the Boost unit test framework, how can I test if my signal has been called?

The mock signal handler

To test the signal handler, we’ll use a functor as a mock signal handler, that sets an internal flag when it gets called. In the functor’s destructor, we’ll do a test on the flag to make sure it got set:

struct mock_handler
{
	mock_handler(const person & expected_person) :
		has_been_called_(false), expected_person_(expected_person) {};

	// The signal handler function.
	void operator()(const person & person)
	{
		has_been_called_ = true;
		BOOST_CHECK_EQUAL(&person == &expected_person_, true);
	};

	// This handler must be called before it goes out of scope.
	~mock_handler()
	{
		BOOST_CHECK_EQUAL(has_been_called_, true);
	};

private:
	bool has_been_called_;
	const person & expected_person_;
};

The test case

Once we’ve written a mock, the test case is pretty simple. Note that I wrap my handler with a boost::ref, so that it doesn’t get copied.

// Test that setting a new name triggers the person.updated signal.
BOOST_AUTO_TEST_CASE(setting_name_triggers_update_signal)
{
	person subject;
	mock_handler handler(subject);

	subject.updated.connect(boost::ref(handler));

	// Change the person's name, triggering the updated signal.
	subject.name("Richard");
}

This works great. And if we comment out the updated signal call in person::name():

Running 1 test case... person_test.cpp(49): error in "setting_name_triggers_update_signal": check has_been_called_ == true failed [0 != 1]

*** 1 failure detected in test suite "tests"

..then the test case will fail accordingly.

June 8th, 2008 | 3 Comments

Warning: this article is no longer being actively updated and parts of it are probably out of date. Proceed with caution!

Update (Nov 2009): the custom log formatter in this article is now part of the Boost Unit Test Library, via the compiler_log_formatter. So you don’t need to roll your own anymore! (Thanks Sean for the tip)

Update 2 (Feb 2010): Xcode 3.2 expects a slightly different syntax for errors. I have updated my example below (courtesy of Nicola Vitacolonna) to match, but Boost.Test’s built-in compiler_log_formatter is currently awaiting a fix.

When writing C++ code, I frequently use the Boost C++ libraries, which includes all sorts of great libraries to complement the C++ Standard Template Library (STL).

Recently I’ve been playing around with Boost’s Test library — in particular, the Unit Test Framework. Here’s a quick guide on how you can integrate it into a project in Xcode, Apple’s IDE.

You’ll need Xcode 3 and Boost 1.35 installed, and a C++ project to play with. For this example, I’m using a C++ Command Line Utility project, which you can download here.

Add a target for the tests executable

First, let’s create a new target for the executable that will run all our tests.

Right click on Targets, and click Add > New Target. Select Shell Tool from the list. Use “Tests” for the target’s name.

On the Build tab of the Target Info window, add Boost’s install paths to the Header and Library Search Paths. On this machine, I used MacPorts to install Boost, which uses /opt/local/include and opt/local/lib, respectively.

Right click on the Tests target, and click Add > Existing Frameworks. Browse and select libboost_unit_test_framework-mt-1_35.dylib from your library search path. Add it to your Tests target.

Tests target info

Now the Boost unit test framework is ready to be used from your Xcode project. Let’s use it!

Writing some test suites

My application has two classes, called a and b. They both have bugs. I’ve created a new group called Tests, and written a simple test suite for each of them.

Here’s my b_tests.cpp file. The examples I’ve used aren’t important, but the case and suite declarations are.

// Tests for the 'b' class.
BOOST_AUTO_TEST_SUITE(b_tests)

BOOST_AUTO_TEST_CASE(subtract_tests)
{
	// Ensure that subtracting 6 from 8 gives 2.
	BOOST_CHECK_EQUAL(b::subtract(8, 6), 2);
}

BOOST_AUTO_TEST_SUITE_END()

First, make sure all your .cpp files are added to the Tests target. Then, to tie all the test files together into a single executable, we’ll use the test framework’s automatic main() feature in our own file called tests_main.cpp:

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE my application tests

#include <boost/test/unit_test.hpp>

Set the active target to Tests. To check everything got wired up correctly, open the debugger window and hit Build and Go. You should see a bunch of tests fail.

Tests in Xcode console debugger

Running tests as part of the build process

Our tests are set up, so let’s integrate them into the build process. Right-click on the Tests target, and click Add > New Run Script Build Phase. Paste the following into the script field (this will resolve to the tests executable):

"${TARGET_BUILD_DIR}/${EXECUTABLE_NAME}"

To keep things obvious, I renamed our new Run Script phase to Run Tests.

Now let’s set up a new rule – the main target should only build after tests have been built and run successfully. Right-click on the MyApp target, click Get Info, and add Tests to the Direct Dependencies list.

Change the active target to MyApp, and hit Build. The tests should fail and return an error code, which Xcode will pick up as a script error.

Xcode run script build error

If the tests don’t succeed, the build will fail, which is exactly what we want.

Parsing the test results

So far, we’ve got the tests running, and integrated into the build process, but the test results output are a bit rough. Let’s fix that.

Xcode will automatically parse script output if it’s prefixed the right way. Unfortunately, none of the Boost Unit Test Framework’s run-time parameters can produce this format. So, we’re going to have to roll up our sleeves and write our own custom unit_test_log_formatter instead.

struct xcode_log_formatter :
	public boost::unit_test::output::compiler_log_formatter
{
	// Produces an Xcode-friendly message prefix.
	void print_prefix(std::ostream& output,
		boost::unit_test::const_string file_name, std::size_t line)
	{
		output << file_name << ':' << line << ": error: ";
	}
};

I’ve chucked this in a file called xcode_log_formatter.hpp in the Tests group. In tests_main.cpp, we’ll use a global fixture to tell the unit test framework to use our Xcode formatter instead of the default.

// Set up the unit test framework to use an xcode-friendly log formatter.
struct xcode_config
{
	xcode_config()
	{
		unit_test_log.set_formatter( new xcode_log_formatter );
	}

	~xcode_config() {}
};

// Call our fixture.
BOOST_GLOBAL_FIXTURE(xcode_config);

After we’ve got this wired up, the test results look much better:

Test failures as Xcode build errors

As you can see, instead of reporting it as a generic script error, Xcode has now recognised both test failures as two individual errors. And instead of hunting around through different files trying to find the line where a test broke, you can simply click on an error, and Xcode will find and highlight it for you.

This makes for much easier development, and allows you to manage unit test failures as easily as standard compile errors.

June 1st, 2008 | 9 Comments

Here’s something I encountered today when writing some C++:

try
{
    throw std::runtime_error("some message");
}
catch (std::exception e)
{
    std::cout << "error: " << e.what() << std::endl;
}

When run, this code will write “error: St9exception”, instead of “some message” to stdout. “St9exception” comes from libstdc++, in which the default value returned by std::exception::what() is the mangled symbol name. The mistake was that I was catching the exception by value, not by reference. (Too much C# perhaps?)

Instead it should have of course been:

try
{
    throw std::runtime_error("some message");
}
catch (const std::exception & e)
{
    std::cout << "error: " << e.what() << std::endl;
}
March 9th, 2008 | 1 Comment