Friday, March 12, 2010

UnitTest++ == teh awesome

Been using UnitTest++ for the last couple months on my work project. So far, enjoying it. Very simple to:
  1. set up
  2. add new tests
  3. explain to the other developers
  4. ?
  5. profit

Our unit tests are part of our project's Visual Studio solution file. Each unit test's project is setup to run the unit test as a post-build step. So far, it's caught a number of 'simple' fixes that broke some.

I've also setup a Hudson server to automatically build our project... But, I didn't want it to fail the build if the tests failed. And, I wanted to collect the XML reports when it's built from the continuous integration server.

So, rather than always just blindly calling UnitTest::RunAllTests() in the unit tests's main() function, I made a utility library that'll look at an environment variable to determine how it should run the tests.

In the unit tests's main() :



int main(int, char const *[])
{
return myUnitTest_runAllTests("my_test");
}

The utility function:




#include "cstdlib"
#include "fstream"
#include "iostream"

#include "boost/filesystem.hpp"

#include "UnitTest++/UnitTest++.h"
#include "UnitTest++/XmlTestReporter.h"


namespace bfs = boost::filesystem;

struct True
{
bool operator()(const UnitTest::Test* const) const
{
return true;
}
};



DLLExport int myUnitTest_runAllTests(const char* const testName)
{
char* xmlDir = 0;
size_t len;
errno_t err = _dupenv_s(&xmlDir,&len,"UNITTEST_XML_DIR");

if (err || len == 0)
{
// env var not set, just run the test w/ standard mechanism
return UnitTest::RunAllTests();
}
else
{
bfs::path p = bfs::path(xmlDir);

// free memory from _dupenv_s
free(xmlDir);

// if necessary, create output directory
if (! bfs::exists(p) || ! bfs::is_directory(p))
{
if (!bfs::create_directories(p))
{
std::cerr << "Problem creating directory " << p << std::endl;
return -1;
}
}

std::string fname(testName);
fname += ".xml";

// use / operator to append filename onto path
bfs::path fpath = p / fname;

std::ofstream f(fpath.file_string().c_str());
UnitTest::XmlTestReporter reporter(f);

UnitTest::TestRunner runner(reporter);

// if we're outputting to XML, don't return failure as return code
// this way tests can fail without it making the automated build think the build failed.
int ret = runner.RunTestsIf(UnitTest::Test::GetTestList(),NULL,True(),0);

return 0;

}
}


And then you just point Hudson's xUnit plugin at the generated reports. Came together suprisingly easy

No comments: