Using the Boost Unit Test Framework with Xcode 3

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 1, 2008

9 Comments

Luca on June 17, 2008 at 10:04 am.

Hi!! Your tutorial have been really useful, but unfortunately it didn’t work to me. What I just needed to add was:
1. goint to Executable/your_executable_name,
2. going on Arguments sheet and
3. setting the environment variable DYLD_LIBRARY_PATH with boost’s lib path.

After this minor setting, I have been able to execute all examples.
Luca

Richard on June 19, 2008 at 9:08 pm.

Was that using my example project? You might have the Boost shared libraries installed in a different folder than me, in which case the references would have broken.

Daryle Walker on July 1, 2008 at 5:22 am.

I think “${TARGET_BUILD_DIR}/${EXECUTABLE_NAME}” is a better script field. Obviously, the latter part avoids hard-coding the name of your testing executable. The former part is better than “BUILT_PRODUCTS_DIR” in this circumstance, at least when I looked at .

Daryle Walker on July 1, 2008 at 5:25 am.

Ah, the URL got cut off, it should be:

http://developer.apple.com/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/build_setting_ref/build_setting_ref.html

By the way, I’m using a Mac OS X 10.4.11 system with Xcode 2.5.

Richard on July 1, 2008 at 8:59 am.

Good point Daryle – post updated :)

Martin Bengtsson on September 6, 2008 at 6:49 am.

Hey! It more or less worked for UnitTest++ as well!

Blacktiger on November 16, 2008 at 2:10 pm.

One thing to remember is to make sure your unit tests are NOT included in the default target. Otherwise you get a bunch of errors about not being able to find the boost libraries.

Jonix on May 7, 2009 at 12:58 am.

This works for UnitTest++ and the UnitTest error messages is in the format Xcode 3.1 wants it, so no additional reformatting was needed.

However, if you have a syntax error in one of your UnitTest cases and the UnitTest suite is not compiled, the script for starting the Test executable is still triggered.

Leave a Reply