一 准备
在sourceforge.net上下载CPPUnit的源代码,地址:http://sourceforge.net/projects/cppunit ,现在最新的版本:cppunit-1.12.0.tar.gz
二 参考
1)看CPPUnit的源代码,在你下载的目录下的src目录下,还有examples目录下的samples。
2)在源码的工程下有一个txt的帮助文件:INSTALL-WIN32.txt, 很不错,但是是英文的。
3)更多的帮助可以查看:
// http://www.vckbase.com/document/viewdoc/?id=1762
// http://www.codeproject.com/library/Using_CPPUnit.asp
// http://cppunit.sourceforge.net/doc/1.8.0/cppunit_cookbook.html#cppunit_cookbook
三 编译(我使用vs编译)
CPPUnit: Unit test 的核心框架库
CPPUnit_dll:作用同上,区别是上面的是静态的lib,此为动态的DLL,我们使用时可以从中任选一个就可以
TestRunner:MFC扩展的DLL,负责Report的GUI浏览
TestPlugInRunner:PlugIn方式运行时,用于运行实现了CPPUnit指定接口的PlugIn DLL
DllPlugInTester:PlugIn方式运行时,在plugIn dll的post event 中使用,在对Plugin dll的编译时候检测结果。
DSPlugIn:vc6需要的addin,当某些case没有通过是,双击可以跳到指定的代码行,VS200*中不需要
四 实例 (只介绍使用宏定义来实现,宏定义使用真是太简单了,其他的原理和类的实现及关系可以参考上面的链接)
1)假设我们要测试的类:
//test data
class SampleClass
{
public:
int Add(int i,int j)
{
return i+j;
}
int Square(int i)
{
return i*i;
}
};
2)我们需要实现相应的测试模块test case:(此类继承CppUnit::TestFixture或CPPUnit::TestCase,可以实现2个虚函数setup()和teardown(),他们在每个测试函数即case的前后自动调用)
class SampleClass_TestCase : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(SampleClass_TestCase);
CPPUNIT_TEST(Add_Test);
CPPUNIT_TEST(Square_Test);
CPPUNIT_TEST_SUITE_END();
public:
void setUp()
{
testClass = new SampleClass();
}
void tearDown()
{
delete testClass;
testClass = NULL;
}
protected:
void Add_Test()
{
CPPUNIT_ASSERT(6 == testClass->Add(1,4));
}
void Square_Test()
{
CPPUNIT_ASSERT(10 == testClass->Square(3));
}
public:
static std::string GetSuiteName();
private:
SampleClass *testClass;
};
std::string SampleClass_TestCase::GetSuiteName()
{
return "TestSample";
}
3)增加上面的testcase到test suite中,当然test suite的名字是可以自己定义的。
//CPPUNIT_TEST_SUITE_REGISTRATION(SampleClassTestCase);
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SampleClass_TestCase,SampleClass_TestCase::GetSuiteName());
4)增加上面的test suite到test runner,testrunner负责所有test suite的管理。可以使用CPPUNIT_REGISTRY_ADD( which, to ) 建立suite的树形关系。比如:CPPUNIT_REGISTRY_ADD( A, B ) ,CPPUNIT_REGISTRY_ADD( B, C )
CPPUNIT_REGISTRY_ADD_TO_DEFAULT(SampleClass_TestCase::GetSuiteName());
5)调用Testrunner,显示结果,一般有三种方式:
第一种:GUI的方式,需要我们建立一个MFC的exe,把下面的代码加入,在我们的theapp::InitInstance()中调用下面的函数,且函数要改为返回true:
// 1) run ui for output in mfc dialog project(.exe)
void TestMain()
{
// declare a test runner, fill it with our registered tests and run them
CppUnit::MfcUi::TestRunner runner;
runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
runner.run();//show UI
}
第二种:console的方式,需要建立一个console的exe,把下面的代码加入,在main中调用下面的函数:
// 2) run console for output in console project(.exe)
void TestMain()
{
// Create the event manager and test controller
CPPUNIT_NS::TestResult controller;
// Add a listener that colllects test result
CPPUNIT_NS::TestResultCollector result;
controller.addListener( &result );
// Add a listener that print dots as test run.
CPPUNIT_NS::BriefTestProgressListener progress;
controller.addListener( &progress );
// Add the top suite to the test runner
CPPUNIT_NS::TestRunner runner;
//runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
runner.run( controller );
// Print test in a compiler compatible format.
CPPUNIT_NS::CompilerOutputter outputter( &result, CPPUNIT_NS::stdCOut() );
outputter.write();
}
第三种:PlugIn的方式,建立一个DLL,加入下面的代码,且必须在此DLL中调用下面的宏之一:(需要注意的是你的dll本身有没有main函数)
我们可以对此DLL的post -event事件中增加对DllPlugInTesterud.exe的调用来在编译的时候就得到结果, 或对编译好的DLL使用TestPlugInRunner来打 开,用GUI的方式查看结果。
// //Implements all the plug-in stuffs, WinMain
CPPUNIT_PLUGIN_IMPLEMENT();
// //or
// //only export function not has main function. if you have main ,use below macro
// CPPUNIT_PLUGIN_EXPORTED_FUNCTION_IMPL( CPPUNIT_NS::TestPlugInDefaultImpl );
四 更多
1)一般需要使用的头文件:
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/mfc/TestRunner.h>
2)一般需要使用的宏定义:
//CPPUNIT_ASSERT(condition): checks condition and throws an exception if it's false.
//CPPUNIT_ASSERT_MESSAGE(message, condition): checks condition and throws an exception and showing
//specified message if it is false.
//CPPUNIT_ASSERT_EQUAL(expected,current): checks if expected is the same as current, and raises exception
//showing expected and current values.
//CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,current): checks if expected is the same as actual, and
//raises exception showing expected and current values, and specified message.
//CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,current,delta): checks if expected and current difference is
//smaller than delta. If it fails, expected and current values are shown.
3)如果我们对想对我们的整个工程下的函数进行单元测试,且想使测试代码和我们的正式代码分离,这时我们可以为我们的测试代码建立一个单独的dll(使用plugin)或exe(输出到GUI或console),在此测试工程中加入我们正式的代码,但是不对我们正式的代码进行拷贝,但是如果我们的project的静态的lib,则我们可以直接调用不需要把代码再加入测试模块中。