It’s been quite a few years since I originally did this comparison. Since then, Charles Nicholson and I created Unit Test++, a C/C++ unit-testing framework that addresses most of my requirements and wish-list items. It’s designed to be a light-weight, high-performance testing framework, particularly aimed at games and odd platforms with limited functionality. It’s definitely my framework of choice and I haven’t looked at new ones in several years because it fits my needs so well. I definitely encourage you to check it out.
One of the topics I’ve been meaning to get to for quite a while is the applicability of test-driven development in games. Every time the topic comes up in conversations or mailing lists, everybody is always very curious about it and they immediately want to know more. I will get to that soon. I promise!
In the meanwhile I’m now in the situation that I need to choose a unit-testing framework to roll out for my team at work. So, before I get to talk about how to use test-driven development in games, or the value of unit testing, or anything like that, we dive deep into a detailed comparison of existing C++ unit-testing frameworks. Hang on tight. It’s going to be a long and bumpy ride with a plot twist at the end.
If you just want to read about a specific framework, you can go directly there:
Overview
How do we choose a unit-testing framework? It depends on what we’re going to do with it and how we’re going to use it. If I used Java for most of my work, the choice would be easy since JUnit seems to be the framework of choice for those working with Java. I don’t hear them arguing over frameworks or proposing new ones very frequently, so it must be pretty good.
Unfortunately that’s not the case with C++. We have our XUnit family member, CppUnit, but we’re clearly not happy with that. We have more unit-testing frameworks than you can shake a stick at. And a lot of teams end up writing their own from scratch. Why is that? Is C++ so inadequate for unit testing that we have trouble fitting the XUnit approach in the language? Not like it’s a bad thing, mind you. Diversity is good. Otherwise I would be stuck writing this under Windows and you would be stuck reading it with Internet Explorer. In any case, I’m clearly not the first one who’s asked this question. This page tries to answer the question, and comes up with some very plausible answers: differences in compilers, platforms, and programming styles. C++ is not exactly a clean, fully supported language, with one coding standard.
A good way to start is to create a list of features that are important given the type of work I expect to be doing. In particular, I want to be doing test-driven development (TDD), which means I’m going to be constantly writing and running many small tests. It’s going to be used for game development, so I’d like to run the tests in a variety of different platforms (PC, Xbox, PS2, next-generation consoles, etc). It should also fit my own personal TDD style (many tests, heavy use of fixtures, etc).
The following list summarizes the features I would like in a unit-testing framework in order of importance. I’ll evaluate each framework on the basis of these features. Thanks to Tom Plunket for providing a slightly different view on the topic that helped me to re-evaluate the relative importance of the different features.
Bonus: Timing support. Both for total running time of tests, and for individual ones. I like to keep an eye on my running times. Not for performance reasons, but to prevent them from getting out of hand. I prefer to keep running time to under 3-4 seconds (it’s the only way to be able to run them very frequently). Ideally, I’d also like to see a warning printed if any single test goes over a certain amount of time.
Easy of installation was not considered a priority; after all, I only have to go through that once—it’s creating new tests that I’m going to be doing all day long. Non-commercial-friendly licenses (like GPL or LGPL) are also not much of an issue because the unit test framework is not something we’re going to link to the executable we ship, so they don’t really impose any restrictions on the final product.
Incidentally, during my research for this article, I found that other people have compiled lists of what they wish for in C++ unit-testing frameworks. It’s interesting to contrast that article with this one and make a note of the differences and similarities between what we’d like to see in a unit test framework.
Ideal Framework
Before I start going over each of the major (and a few minor) C++ unit-testing frameworks, I decided I would apply the philosophy behind test-driven development to this analysis and start by thinking what I would like to have. So I decided to write the set of sample tests in some ideal unit-testing framework without regard for language constrains or anything. In the ideal world, this is what I would like my unit tests to be like.
The simplest possible test should be trivial to create. Just one line to declare the test and then the test body itself:
TEST (SimplestTest) { float someNum = 2.00001f; ASSERT_CLOSE (someNum, 2.0f); }
A test with fixtures is going to be a bit more complicated, but it should still be really easy to set up:
SETUP (FixtureTestSuite) { float someNum = 2.0f; std::string str = "Hello"; MyClass someObject("somename"); someObject.doSomethng(); } TEARDOWN (FixtureTestSuite) { someObject.doSomethingElse(); } TEST (FixtureTestSuite, Test1) { ASSERT_CLOSE (someNum, 2.0f); someNum = 0.0f; } TEST (FixtureTestSuite, Test2) { ASSERT_CLOSE (someNum, 2.0f); } TEST (FixtureTestSuite, Test3) { ASSERT_EQUAL(str, "Hello"); }
The first thing to point out about this set of tests is that there is a minimum amount of code spent in anything other than the tests themselves. The simplest possible test takes a couple of lines and needs no support other than a main file that runs all the tests. Setting up a fixture with setup/teardown calls should also be totally trivial. I don’t want to inherit from any classes, override any functions, or anything. Just write the setup step and move on.
Look at the setup function again. The variables that are going to be used in the tests are not dynamically created. Instead, they appear to be declared on the stack and used directly there. Additionally, I should point out that those objects should only be created right before each test, and not before all tests start. How exactly are the tests going to use them? I don’t know, but that’s what I would like to use. That’s why this is an ideal framework.
Now let’s contrast it to six real unit-testing frameworks that have to worry about actually compiling and running. For each of the frameworks I look at the list of wanted features and I try to implement the two tests I implemented with this ideal framework. Here is the source code for all the examples.
CppUnit is probably the most widely used unit-testing framework in C++, so it’s going to be a good reference to compare other unit tests against. I had used CppUnit three or four of years ago and my impressions back then were less than favorable. I remember the code being a mess laced with MFC, the examples all tangled up with the framework, and the silly GUI bar tightly coupled with the software. I even ended up creating a patch to provide console-only output and removed MFC dependencies. So this time I approached it with a bit of apprehension to say the least.
I have to admit that CppUnit has come a long way since then. I was expecting the worst, but this time I found it much easier to use and configure. It’s still not perfect, but it’s much, much better than it used to. The documentation is pretty decent, but you’ll have to end up digging deep into the module descriptions to even find out that some functionality is available.
// Simplest possible test with CppUnit #include class SimplestCase : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( SimplestCase ); CPPUNIT_TEST( MyTest ); CPPUNIT_TEST_SUITE_END(); protected: void MyTest(); }; CPPUNIT_TEST_SUITE_REGISTRATION( SimplestCase ); void SimplestCase::MyTest() { float fnum = 2.00001f; CPPUNIT_ASSERT_DOUBLES_EQUAL( fnum, 2.0f, 0.0005 ); }
#include #include "MyTestClass.h" class FixtureTest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( FixtureTest ); CPPUNIT_TEST( Test1 ); CPPUNIT_TEST( Test2 ); CPPUNIT_TEST( Test3 ); CPPUNIT_TEST_SUITE_END(); protected: float someValue; std::string str; MyTestClass myObject; public: void setUp(); protected: void Test1(); void Test2(); void Test3(); }; CPPUNIT_TEST_SUITE_REGISTRATION( FixtureTest ); void FixtureTest::setUp() { someValue = 2.0; str = "Hello"; } void FixtureTest::Test1() { CPPUNIT_ASSERT_DOUBLES_EQUAL( someValue, 2.0f, 0.005f ); someValue = 0; //System exceptions cause CppUnit to stop dead on its tracks //myObject.UseBadPointer(); // A regular exception works nicely though myObject.ThrowException(); } void FixtureTest::Test2() { CPPUNIT_ASSERT_DOUBLES_EQUAL( someValue, 2.0f, 0.005f ); CPPUNIT_ASSERT_EQUAL (str, std::string("Hello")); } void FixtureTest::Test3() { // This also causes it to stop completely //myObject.DivideByZero(); // Unfortunately, it looks like the framework creates 3 instances of MyTestClass // right at the beginning instead of doing it on demand for each test. We would // have to do it dynamically in the setup/teardown steps ourselves. CPPUNIT_ASSERT_EQUAL (1, myObject.s_currentInstances); CPPUNIT_ASSERT_EQUAL (3, myObject.s_instancesCreated); CPPUNIT_ASSERT_EQUAL (1, myObject.s_maxSimultaneousInstances); }
Overall, CppUnit is frustrating because it’s almost exactly what I want, except for my most wanted feature. I really can’t believe that it takes so much typing (and duplicated typing at that) to add new tests. Other than that, the main complaint is the need for RTTI or exceptions, and the relative complexity of the source code, which could make it challenging to port to different platforms.
Update: I’ve revised my comments and ratings of the Boost.Test framework in light of the comments from Gennadiy Rozental pointing out how easy it is to add fixtures in boost.
I’m a big fan of Boost, but I have to admit that it wasn’t until about a year ago that I even learned that Boost was providing a unit testing library. Clearly, I had to check it out.
The first surprise is that Boost.Test isn’t exclusively a unit-testing framework. It also pretends to be a bunch of other things related to testing. Nothing terribly wrong with that, but to me is the first sign of a “smell.” The other surprise is that it wasn’t really based on the XUnit family. Hmmm… In that case, it had better provide some outstanding functionality.
The documentation was top notch. Some of the best I saw for any testing framework. The concepts were clearly explained, and had lots of simple examples to demonstrate different features. Interestingly, from the docs I saw that Boost.Test was designed to support some things that I would consider bad practices such as dependencies between tests, or long tests.
#include #include BOOST_AUTO_UNIT_TEST (MyFirstTest) { float fnum = 2.00001f; BOOST_CHECK_CLOSE(fnum, 2.f, 1e-3); }
#include #include #include "MyTestClass.h" struct MyFixture { MyFixture() { someValue = 2.0; str = "Hello"; } float someValue; std::string str; MyTestClass myObject; }; BOOST_AUTO_UNIT_TEST (TestCase1) { MyFixture f; BOOST_CHECK_CLOSE (f.someValue, 2.0f, 0.005f); f.someValue = 13; } BOOST_AUTO_UNIT_TEST (TestCase2) { MyFixture f; BOOST_CHECK_EQUAL (f.str, std::string("Hello")); BOOST_CHECK_CLOSE (f.someValue, 2.0f, 0.005f); // Boost deals with this OK and reports the problem //f.myObject.UseBadPointer(); // Same with this //myObject.DivideByZero(); } BOOST_AUTO_UNIT_TEST (TestCase3) { MyFixture f; BOOST_CHECK_EQUAL (1, f.myObject.s_currentInstances); BOOST_CHECK_EQUAL (3, f.myObject.s_instancesCreated); BOOST_CHECK_EQUAL (1, f.myObject.s_maxSimultaneousInstances); }
#include #include using boost::unit_test::test_suite; struct MyTest { void TestCase1() { float fnum = 2.00001f; BOOST_CHECK_CLOSE(fnum, 2.f, 1e-3); } void TestCase2() {} }; test_suite * GetSuite1() { test_suite * suite = BOOST_TEST_SUITE("my_test_suite"); boost::shared_ptr instance( new MyTest() ); suite->add (BOOST_CLASS_TEST_CASE( &MyTest::TestCase1, instance )); suite->add (BOOST_CLASS_TEST_CASE( &MyTest::TestCase2, instance )); return suite; }
#include using boost::unit_test::test_suite; extern test_suite * GetSuite1(); boost::unit_test::test_suite* init_unit_test_suite( int /* argc */, char* /* argv */ [] ) { test_suite * test = BOOST_TEST_SUITE("Master test suite"); test->add( boost::unit_test::ut_detail::auto_unit_test_suite() ); test->add(GetSuite1()); return test; }
Boost.Test is a library with a huge amount of potential. It has great support for exception handling and advanced assert statements. It also has other fairly unique functionality such as support for checking for infinite loops, and different levels of logging. On the other hand, it’s very verbose to add new tests that are part of a suite, and it might be a bit heavy weight for game console environments.
CppUnitLite has a funny story behind it. Michael Feathers, the original author of CppUnit, got fed up with the complexity of CppUnit and how it didn’t fit everyone’s needs, so we wrote the ultra-light weight framework CppUnitLite. It is as light on the features as it is on complexity and size, but his philosophy was to let people customize it to deal with whatever they need.
Indeed, CppUnitLite is only a handful of files and it probably adds up to about 200 lines of very clear, easy to understand and modify code. To be fair, in this comparison I actually used a version of CppUnitLite I modified a couple of years ago (download it along with all the sample code) to add some features I needed (fixtures, exception handling, different outputs). I figured it was definitely in the spirit that CppUnitLite was intended, and if nothing else, it can show what can be accomplished by just a few minutes of work with the source code.
On the other hand, CppUnitLite doesn’t have any documentation to speak of. Heck, it doesn’t even have a web site of its own, which I’m sure is not helping the adoption rate by other developers.
Minimal amount of work needed to add new tests. Absolutely! Of all the unit-test frameworks, this is the one that comes closest to the ideal. On the other hand, it could be the fact that I’ve used CppUnitLite the most and I’m biased. In any way, it really fits my idea of minimum amount of work required to set up a simple test or even one with a fixture (although that could be made even better).
#include "lib/TestHarness.h" TEST (Whatever,MyTest) { float fnum = 2.00001f; CHECK_DOUBLES_EQUAL (fnum, 2.0f); }
Easy to modify and port. Definitely. Again, it gets best of the class award in this category. No other unit-test framework comes close to being this simple, easy to modify and port, and at the same time having reasonably well separated functionality. The original version of CppUnitLite even had a special lightweight string class to avoid dependencies on STL. In my modified version I changed it to use std::string since that’s what I use in most of my projects, but the change took under one minute to do. Also, using it under Linux was absolutely trivial, even though I had only used it under Windows before.
Supports fixtures. This is where the original CppUnitLite starts running into trouble. It’s so lightweight that it doesn’t have room for many features. This was an absolute must for me, so I went ahead and added it. I’m sure it could be improved to make it so adding a fixture requires even less typing, but it’s functional as it stands. Unfortunately, it suffers from the problem that objects need to be created dynamically if we want them to be created right before each test. To be fair though, every single unit-test framework in this evaluation has that requirement. Oh well.
#include "lib/TestHarness.h" #include "MyTestClass.h" class MyFixtureSetup : public TestSetup { public: void setup() { someValue = 2.0; str = "Hello"; } void teardown() {} protected: float someValue; std::string str; MyTestClass myObject; }; TESTWITHSETUP (MyFixture,Test1) { CHECK_DOUBLES_EQUAL (someValue, 2.0f); someValue = 0; // CppUnitLite doesn't handle system exceptions very well either //myObject.UseBadPointer(); // A regular exception works nicely though myObject.ThrowException(); } TESTWITHSETUP (MyFixture,Test2) { CHECK_DOUBLES_EQUAL (someValue, 2.0f); CHECK_STRINGS_EQUAL (str, std::string("Hello")); } TESTWITHSETUP (MyFixture,Test3) { // Unfortunately, it looks like the framework creates 3 instances of MyTestClass // right at the beginning instead of doing it on demand for each test. We would // have to do it dynamically in the setup/teardown steps ourselves. CHECK_LONGS_EQUAL (1, myObject.s_currentInstances); CHECK_LONGS_EQUAL (3, myObject.s_instancesCreated); CHECK_LONGS_EQUAL (1, myObject.s_maxSimultaneousInstances); }
Handles exceptions and crashes well. The original CppUnitLite didn’t handle them at all. I added minor support for this (just an optional try/catch). To run the tests without exception support it requires recompiling the tests with a special define turned on, so it’s not at slick as the command-line argument that Boost.Test features.
Good assert functionality. Here is where CppUnitLite really shows its age. The assert macros are definitely the worst of the lot. They don’t use a stream to print out the contents of their variables, so we need custom macros for each object type you want to use. It comes with support for doubles, longs, and strings, but anything else you need to add by hand. Also, it doesn’t have any checks for anything other than equality (or closeness in the case of floating-point numbers).
Supports different outputs. Again, the original only had one type of output. But it was very well isolated and it was trivial to add more.
Supports suites. Probably the only framework that doesn’t support suites. I never really needed them, but they would probably be very easy to add on a per-file basis.
CppUnitLite is as barebones as it gets, but with a few modifications it hits the mark in all the important categories. If it had better support for assert statements, it would come very close to my ideal framework. Still, it’s a worthy candidate for the final crown.
I had never heard of NanoCppUnit until Phlip brought it up. From reading the feature list, it really appeared to be everything that I wanted CppUnitLite to be, except that it was better and ready to work out of the box.
The first point against NanoCppUnit is the awful “packaging” of the framework. If you thought that CppUnitLite was bad (not having a web page of its own), well, at least you could download it as a zip file. For NanoCppUnit you actually have to copy and paste the five files that make up the framework from a web page. I’m not kidding. That makes for some “lovely” formatting issues I might add. The documentation found in the web page wasn’t exactly very useful either.
In any case, I continued my quest to get a simple test program up and running with NanoCppUnit. Out of the box (or out of the web page, rather) it’s clearly aimed only at Windows platforms. I thought it would be trivial to fix, but changing it required more time than I thought at first (I personally gave up when I started getting errors buried three macros deep into some assert statement). Unlike CppUnitLite, the source code is not very well structured at all, full of ugly macros everywhere, making it not trivial to add new features like new output types. Unless I’m totally mistaken, it even looks like it has sample code inside the test framework itself. Eventually I had to give up on running it under Linux, so my comments here are just best guesses by looking at the source code.
struct MySuite: TestCase { }; TEST_(MySuite, MyTest) { float fnum = 2.00001f; CPPUNIT_ASSERT_DOUBLES_EQUAL(fnum, 2.0f, 0.0001); }
One of NanoCppUnit’s unique features is regular expression support as part of its assert tests. That’s very unusual, but I can see how it could come in handy. A few times in the past, I’ve had to check that a certain line of code has some particular format, so I had to sscanf it, and then check on some of the contents. A regular expression check would have done the job nicely.
Unfortunately, NanoCppUnit doesn’t really live up to the standards of other frameworks. Right now it feels too much as a work in progress, with too much missing functionality and not clearly structured code.
The further along we get in this evaluation, the less Xunit-like the frameworks become. Unit++’s unique feature is that it pretends to be more C++-like than CppUnit. Wait a second, did I hear that right? More C++ like? Is that supposed to be a good thing? Looking back at my ideal test framework, it really isn’t very much like C++ at all. Once I started thinking about that topic I realized that there really is no reason at all why the tests framework itself needs to be in C++. The tests you write need to be in the language of the code being tested, but all the wrapper code doesn’t. That’s a point that the next, and final, testing framework will drive home.
So, what does it mean to be more C++ like? No macros for a start. You create suites of tests by creating classes that derive from suite. That’s the same thing we were doing in most other frameworks, really, but it was just happening behind the scenes. It really doesn’t help me any to know that that is what I’m doing, and I would certainly not call it a “feature.” As a result, tests are more verbose than they could be.
The documentation is simply middle-of-the-road. It’s there, but it’s not particularly detailed and it doesn’t come loaded with examples.
#define __UNITPP #include "unit++.h" using namespace unitpp; namespace { class Test : public suite { void test1() { float fnum = 2.00001f; assert_eq("Checking floats", fnum, 2.0f); } public: Test() : suite("MySuite") { add("test1", testcase(this, "Simplest test", &Test::test1)); suite::main().add("demo", this); } }; Test* theTest = new Test(); }
Overall, Unit++ is not really a candidate. Perhaps it’s because it’s not intended for the type of testing I intend to use it for, but it doesn’t offer anything new over other frameworks and it has a lot of drawbacks of its own. The lack of fixtures is simply unforgivable.
After looking into a framework that tried to be different from XUnit (Unit++), I wasn’t particularly looking forward to evaluating possibly the wackiest one of them all, CxxTest. I had never heard of it until a few days ago, but I knew that it required using Perl along the way to generate some C++ code. My spider senses were tingling.
Boy was I wrong!! Within minutes of using CxxTest and reading through its great documentation (the best by far), I was completely convinced this was the way to go. This came as a complete surprise to me since I was ready to leave somewhat dissatisfied and pronounce a victor between CppUnit and CppUnitLite.
Let’s start from the beginning. What’s with the use of Perl and why is it different from CppUnit? Erez Volk, the author of CxxTest, had the unique insight that just because we’re testing a C++ program, we don’t need to rely on C++ for everything. Other languages, such as Java, are better suited to what we want to do in a unit-testing framework because they have good introspection (reflection) capabilities. C++ is quite lacking in that category, so we’re forced to use kludges like manual registration of tests, ugly macros, etc. CxxTest gets around that by parsing our simple tests and generating a C++ test runner that calls directly into our tests. The result is simply brilliant. We get all the flexibility we need without the need for any ugly macros, exotic libraries, or fancy language features. As a matter of fact, CxxTest’s requirements are as plain vanilla as you can get (other than being able to run Perl).
The code-generation step is also trivial to integrate into the regular build system. The wonderful documentation gives explicit step-by-step instructions on how to integrate it with make files, Visual Studio projects files, or Cons. Once you have it set up, you won’t even remember there’s anything out of the ordinary going on.
Let’s see how it stacks up against the competition.
class SimplestTestSuite : public CxxTest::TestSuite { public: void testMyTest() { float fnum = 2.00001f; TS_ASSERT_DELTA (fnum, 2.0f, 0.0001f); } };
#include "MyTestClass.h" class FixtureSuite : public CxxTest::TestSuite { public: void setUp() { someValue = 2.0; str = "Hello"; } void tearDown() {} void test1() { TS_ASSERT_DELTA (someValue, 2.0f, 0.0001f); someValue = 13.0f; // A regular exception works nicely though myObject.ThrowException(); } void test2() { TS_ASSERT_DELTA (someValue, 2.0f, 0.0001f); TS_ASSERT_EQUALS (str, std::string("Hello")); } void test3() { //myObject.UseBadPointer(); TS_ASSERT_EQUALS (1, myObject.s_currentInstances); TS_ASSERT_EQUALS (3, myObject.s_instancesCreated); TS_ASSERT_EQUALS (1, myObject.s_maxSimultaneousInstances); } float someValue; std::string str; MyTestClass myObject; };
Another feature supported by CxxUnit that I haven’t had time to look into is some support for mock objects. Anybody doing TDD knows the value of mock objects when it comes to testing the interactions between a set of objects. Apparently CxxUnit allows you to override global functions with specific mock functions (it gives an example of overriding fopen()). I don’t think it helps any with regular classes; for those you’re on your own.
So, what’s not to like in CxxTest? Not much, really. Other than wishing that the test syntax were a bit tighter, the only thing to watch out for is what happens with large projects. If you follow the examples in the documentation, it will create a single runner for all the tests you give it. This can be problematic if you’re going to be having thousands of tests, and then making one small change in one of them causes a full recompilation of all your code.
Update: After talking with Erez and re-checking the documentation, I realized this is already fully supported in CxxUnit. By default, when you generate a test runner, it adds a main function and some global variables, so linking with other similar runners gives all sorts of problems. However, it turns out you can generate a test runner with the –part argument, and it will leave out the main function and any other globals. You can then link together all the runners and have a single executable. I wonder if it would be worth going as far as creating a runner for every suite, or if it would be best to cluster suites together. Worth investigating at some point whenever I get enough tests to make a difference.
Conclusion
After going through all six C++ unit-testing frameworks, four stand out as reasonable candidates: CppUnit, Boost.Test, a modified CppUnitLite, and CxxTest.
Of the four, CxxTest is my new personal favorite. It fits very closely the requirements of my ideal framework by leveraging the power of an external scripting language. It’s very usable straight out of the “box” and it provides some nifty advanced features and great assert functionality. It does require the use of a scripting language as part of the build process, so those unconfortable with that requirement, might want to look at one of the other three frameworks.
CppUnit is a solid, complete framework. It has come a long, long way in the last few years. The major drawbacks are the relative verbosity for adding new tests and fixtures, as well as the reliance on STL and some advanced language issues.
If what you need is absolute simplicity, you can do no wrong starting with CppUnitLite (or a modified version), and tweaking it to fit your needs. It’s a well-structured, ultra-light framework with no external dependencies, so modifying it is extremely easy. Its main drawback is the lack of features and the primitive assert functionality.
If you’re going to be working mostly on the PC, you don’t expect to have to modify the framework itself, and you don’t mind pulling in some additional Boost libraries, Boost.Test could be an excellent choice.
Should you roll your own unit-test framework? I know that Kent Beck recommends it in his book Test-Driven Development: By Example, and it might be a great learning experience, but I just can’t recommend it. Just as it’s probably good to write a linked-list and a stack data structure a few times but I wouldn’t recommend actually doing that in production code instead of using the ones provided in the STL, I strongly recommend starting with one of the three unit-testing frameworks mentioned above. If you really feel the need to roll your own, grab CppUnitLite and get hacking.
Whichever one you choose, you can really do no wrong with one of those three frameworks. The most important thing is that you are writing unit tests, or, even better, doing test-driven development. To paraphrase Michael Feathers, code without unit tests is legacy code, and you don’t want to be writing legacy code, do you?
unit_test_frameworks.tar.gz
From http://gamesfromwithin.com/exploring-the-c-unit-testing-framework-jungle