前言
CppUnit是一個開源的單元測試框架,支持Linux和Windows操作系統,在linux上可以直接進行源碼編譯,得到動態庫和靜態庫,直接鏈接就可以正常使用,在Windows上可以使用VC直接進行編譯,非常便於調試。CppUnit的源碼框架被運用到了Java和Python等語言中,使用非常廣泛,熟悉了一種語言下的CppUnit使用方法,其他語言測試框架也不在話下,本文以cppunit-1.12.1為例進行演示和說明。
一個例子
Linux下CppUnit源碼編譯和安裝
- 解壓源碼文件到cppunit-1.12.1目錄
- cd cppunit-1.12.1
- ./configure --prefix=安裝路徑(必須是絕對路徑)
- make
- make install
編輯測試代碼
一共三個文件main.cpp、simpleTest.h、simpleTest.c,目錄下文件的組織結構如下所示:

三個文件的源碼如下:
//main.cpp文件
#include "cppunit/TestResultCollector.h"
#include "cppunit/TextOutputter.h"
#include "cppunit/XmlOutputter.h"
#include "cppunit/CompilerOutputter.h"
#include "cppunit/TestResult.h"
#include "cppunit/TestRunner.h"
#include "cppunit/extensions/TestFactoryRegistry.h"
#include <cstdlib>
#include <ostream>
int main()
{
CppUnit::TestResult r;
CppUnit::TestResultCollector rc;
r.addListener(&rc); // 准備好結果收集器
CppUnit::TestRunner runner; // 定義執行實體
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry("alltest").makeTest());
runner.run(r); // 運行測試
//CppUnit::TextOutputter o(&rc, std::cout);
//o.write(); // 將結果輸出
//std::ofstream file;
//file.open("./UnitTest.xml");
//CppUnit::XmlOutputter xo(&rc, file);
//xo.write();
CppUnit::CompilerOutputter co(&rc, std::cout);
co.write();
return rc.wasSuccessful() ? 0 : -1;
}
//SimpleTest .h文件
#include "cppunit/extensions/HelperMacros.h"
class SimpleTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(SimpleTest);
CPPUNIT_TEST(test1);
CPPUNIT_TEST(test2);
CPPUNIT_TEST_SUITE_END();
public:
void test1();
void test2();
};
//simpleTest.cpp文件
#include "simpleTest.h"
#include <string>
#include <iostream>
#include "cppunit/TestCase.h"
#include "cppunit/TestAssert.h"
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SimpleTest, "alltest");
void SimpleTest::test2()
{
CPPUNIT_ASSERT(3 == 3);
}
void SimpleTest::test1()
{
CPPUNIT_ASSERT(2 == 2);
}
編譯命令如下:
g++ main.cpp simpleTest.cpp -o test -I /home/chusiyong/cppunit/install/include -L /home/chusiyong/cppunit/install/lib -Wl,-Bstatic -lcppunit -Wl,-Bdynamic -ldl
運行可執行文件,結果如下:
OK (2)
表示所有用例都執行成功
源碼分析
1.主要類的繼承關系
- Test相關類

-
- Test類作為所有測試用例的基類,是一個抽象類,含有純虛函數
- Test類主要的方法是run方法
- 采用Composition設計模式(類比文件和文件夾的設計方法)
- TestComposition類主要是實現類名的處理,並提供start和end的處理(配合TestListener使用)
- TestSuite類非常重要,里面可以包含多個Test類,使用Vector的方式保存
- TestFixture類主要提供setUp和tearDown方法,用於運行測試用例前和測試用例后執行
- TestLeaf類主要是實現Test類中的部分方法,在體現通用性的同時做安全措施,重寫必要的virtual方法(實現Test類中模板方法中調用的函數),繼承該類的子類只能是單個測試用例,不能包含子測試用例
- TestCase類是主要的測試類,每一個TestCase表示一個測試用例
- TestCaller類主要用來生成TestCase實例,將TestFixture類中的每一個測試方法,變成一個單獨的TestCase實例(很重要),然后將TestCase實例加入到TestSuite中
//TestComposite.h
class CPPUNIT_API TestComposite : public Test
{
public:
TestComposite( const std::string &name = "" );
~TestComposite();
void run( TestResult *result );
std::string getName() const;
private:
const std::string m_name;
};
//TestComposite.cpp
void TestComposite::run( TestResult *result )
{
...
doRunChildTests( result );
...
}
void TestComposite::doRunChildTests( TestResult *controller)
//關鍵方法,調用每一個子用例的run方法
{
int childCount = getChildTestCount();
for ( int index =0; index < childCount; ++index )
{
if ( controller->shouldStop() )
break;
getChildTestAt( index )->run( controller );
}
}
std::string TestComposite::getName() const //獲取測試用例名稱
{
return m_name;
}
//TestSuite.h
class CPPUNIT_API TestSuite : public TestComposite
{
public:
TestSuite( std::string name = "" );
~TestSuite();
void addTest( Test *test ); //添加測試用例
virtual void deleteContents(); //刪除測試用例
int getChildTestCount() const; //根據vector獲取子用例個數
Test *doGetChildTestAt( int index ) const;//根據index獲取子用例對象
private:
CppUnitVector<Test *> m_tests; //保存子用例
};
//TestSuite.cpp
void TestSuite::deleteContents() //刪除所有測試用例
{
int childCount = getChildTestCount();
for ( int index =0; index < childCount; ++index )
delete getChildTestAt( index );
m_tests.clear();
}
void TestSuite::addTest( Test *test ) //添加測試用例
{
m_tests.push_back( test );
}
int TestSuite::getChildTestCount() const //獲取子測試用例的個數
{
return m_tests.size();
}
Test *TestSuite::doGetChildTestAt( int index ) const//根據index獲取子用例對象
{
return m_tests[index];
}
//TestLeaf.h 沒有實現run方法,不能生成實例對象
class CPPUNIT_API TestLeaf: public Test
{
public:
int countTestCases() const; //Test類中的checkIsValidIndex方法調用
int getChildTestCount() const;//Test類中的checkIsValidIndex方法調用
Test *doGetChildTestAt( int index ) const;//Test類中的getChildTestAt方法調用
};
//TestLeaf.cpp
int TestLeaf::countTestCases() const
{
return 1;
}
int TestLeaf::getChildTestCount() const
{
return 0;
}
Test *TestLeaf::doGetChildTestAt( int index ) const
{
checkIsValidIndex( index );
return NULL; // never called, checkIsValidIndex() always throw.
}
//TestFixture.h
class CPPUNIT_API TestFixture //接口
{
public:
virtual ~TestFixture() {};
virtual void setUp() {};//運行用例前調用
virtual void tearDown() {};//運行用例后調用
};
//TestCase.h
class CPPUNIT_API TestCase : public TestLeaf, public TestFixture
{
public:
TestCase( const std::string &name );
TestCase();
~TestCase();
virtual void run(TestResult *result); //實現純虛方法
std::string getName() const; //獲取用例名稱
virtual void runTest(); //子類實現
private:
TestCase( const TestCase &other );
TestCase &operator=( const TestCase &other );
private:
const std::string m_name;
};
//TestCase.cpp
//說明:運行測試用例的時候,是采用的保護性運行方式,保證一個用例執行失敗后續的用例可以繼續執行
//采用try{...}catch{...}的模式,失敗就拋異常,然后記錄,繼續執行
void TestCase::run( TestResult *result )
{
result->startTest(this);
if ( result->protect( TestCaseMethodFunctor( this, &TestCase::setUp ), this, "setUp() failed" ))
{
result->protect( TestCaseMethodFunctor( this, &TestCase::runTest ), this);
}
result->protect( TestCaseMethodFunctor( this, &TestCase::tearDown ), this, "tearDown() failed");
result->endTest( this );
}
-
- TestCaseMethodFunctor類:函數對象,作用就是封裝方法,便於使用
class TestCaseMethodFunctor : public Functor
{
public:
typedef void (TestCase::*Method)();
TestCaseMethodFunctor( TestCase *target,
Method method )
: m_target( target )
, m_method( method )
{
}
bool operator()() const //重載()操作符
{
(m_target->*m_method)(); //直接調用測試用例的地方
return true;
}
private:
TestCase *m_target;
Method m_method;
};
template <class Fixture>
class TestCaller : public TestCase
{
public:
TestCaller( std::string name, TestMethod test ) :
TestCase( name ),
m_ownFixture( true ),
m_fixture( new Fixture() ),
m_test( test )
{
}
TestCaller(std::string name, TestMethod test, Fixture& fixture) :
TestCase( name ),
m_ownFixture( false ),
m_fixture( &fixture ),
m_test( test )
{
}
TestCaller(std::string name, TestMethod test, Fixture* fixture) :
TestCase( name ),
m_ownFixture( true ),
m_fixture( fixture ),
m_test( test )
{
}
~TestCaller()
{
if (m_ownFixture)
delete m_fixture;
}
void runTest()
{
(m_fixture->*m_test)(); //運行測試用例的地方
}
void setUp()
{
m_fixture->setUp ();
}
void tearDown()
{
m_fixture->tearDown ();
}
std::string toString() const
{
return "TestCaller " + getName();
}
private:
TestCaller( const TestCaller &other );
TestCaller &operator =( const TestCaller &other );
private:
bool m_ownFixture;
Fixture *m_fixture; //new出來的測試對象,即TestCase
typedef void (Fixture::*TestMethod)();
TestMethod m_test;//Testcase中的一個測試方法
};
- TestListener相關類

-
- TestListener類和TestResult類之間是采用觀察者模式,TestResult類將測試用例的執行結果通知給TestListener類
- TestListener類將保持的結果,通過OutPutter類顯示出來
- TestSuccessListener類主要作用是實現多線程安全
class CPPUNIT_API TestListener
{
public:
virtual ~TestListener() {}
virtual void addFailure( const TestFailure & /*failure*/ ) {}
//主要的函數,當測試用例執行失敗時,調用該接口將結果保持到觀察者實例中
};
//TestSuccessListener.h
class CPPUNIT_API TestSuccessListener : public TestListener,
public SynchronizedObject
{
public:
TestSuccessListener( SynchronizationObject *syncObject = 0 );
virtual ~TestSuccessListener();
virtual void reset();
void addFailure( const TestFailure &failure ); //添加失敗信息
virtual bool wasSuccessful() const; //判斷執行結果
private:
bool m_success;
};
//TestSuccessListener.cpp
void TestSuccessListener::addFailure( const TestFailure &failure )
{
ExclusiveZone zone( m_syncObject ); //多線程時的鎖
m_success = false;
}
bool TestSuccessListener::wasSuccessful() const
{
ExclusiveZone zone( m_syncObject );
return m_success;
}
void TestSuccessListener::reset()
{
ExclusiveZone zone( m_syncObject );
m_success = true;
}
//TestResultCollector.h
class CPPUNIT_API TestResultCollector : public TestSuccessListener
{
public:
TestResultCollector( SynchronizationObject *syncObject = 0 );
virtual ~TestResultCollector();
void addFailure( const TestFailure &failure );
virtual void reset();
virtual int testErrors() const;
virtual int testFailures() const;
virtual int testFailuresTotal() const;
virtual const TestFailures& failures() const;
protected:
void freeFailures();
typedef CppUnitDeque<Test *> Tests;
Tests m_tests;
typedef CppUnitDeque<TestFailure *> TestFailures;
TestFailures m_failures;
int m_testErrors;
};
//TestResultCollector.cpp
void TestResultCollector::freeFailures() //釋放所有錯誤信息
{
TestFailures::iterator itFailure = m_failures.begin();
while ( itFailure != m_failures.end() )
delete *itFailure++;
m_failures.clear();
}
void TestResultCollector::reset() //將Listener的狀態變成初始狀態
{
TestSuccessListener::reset();
ExclusiveZone zone( m_syncObject );
freeFailures();
m_testErrors = 0;
m_tests.clear();
}
void TestResultCollector::addFailure( const TestFailure &failure )//添加錯誤信息
{
TestSuccessListener::addFailure( failure );
ExclusiveZone zone( m_syncObject );
if ( failure.isError() )
++m_testErrors;
m_failures.push_back( failure.clone() );
}
int TestResultCollector::testFailuresTotal() const //返回錯誤信息的個數(包括error)
{
ExclusiveZone zone( m_syncObject );
return m_failures.size();
}
int TestResultCollector::testFailures() const //返回失敗用例的個數(不包括error)
{
ExclusiveZone zone( m_syncObject );
return m_failures.size() - m_testErrors;
}
//返回錯誤信息
const TestResultCollector::TestFailures & TestResultCollector::failures() const
{
ExclusiveZone zone( m_syncObject );
return m_failures;
}
//返回error的個數
int TestResultCollector::testErrors() const
{
ExclusiveZone zone( m_syncObject );
return m_testErrors;
}
-
- TestFailure類:用於表示測試用例的執行結果,一個測試用例執行失敗就會生成一個TestFailure類的實例
- TestFailure可以表示用例執行失敗,也可以表示error,二者的區別是:測試用例執行失敗時拋出的異常是已知的,如果執行用例時拋出未知異常,就是error
//TestFailure.cpp.h
class CPPUNIT_API TestFailure
{
public:
TestFailure( Test *failedTest, Exception *thrownException, bool isError );
virtual ~TestFailure ();
virtual Test *failedTest() const; //返回失敗用例對象
virtual Exception *thrownException() const; //返回拋出的對象
virtual SourceLine sourceLine() const; //獲取拋出異常的代碼行號
virtual bool isError() const; //判斷是用例失敗還是error
virtual std::string failedTestName() const;//獲取失敗測試用例的名稱
virtual TestFailure *clone() const;//克隆
protected:
Test *m_failedTest;
Exception *m_thrownException;
bool m_isError;
private:
TestFailure( const TestFailure &other );
TestFailure &operator =( const TestFailure& other );
};
//TestFailure.cpp
TestFailure::TestFailure( Test *failedTest,
Exception *thrownException,
bool isError ) :
m_failedTest( failedTest ), //失敗的用例
m_thrownException( thrownException ), //拋出的異常
m_isError( isError )//是用例失敗還是未知異常
{
}
- 多線程安全同步機制
-
-
SynchronizationObject類似一個基類,提供lock和unlock的接口,可以依據不同的平台進行繼承實現互斥鎖
-
ExclusiveZone類的作用是封裝SynchronizationObject類,方便使用互斥鎖,關鍵就是在構造函數中調用lock函數,析構函數中調用unlock函數,無需手動調用lock和unlock函數
class CPPUNIT_API SynchronizedObject
{
public:
class SynchronizationObject //實現互斥鎖的基類
{
public:
SynchronizationObject() {}
virtual ~SynchronizationObject() {}
virtual void lock() {}
virtual void unlock() {}
};
SynchronizedObject( SynchronizationObject *syncObject =0 );
virtual ~SynchronizedObject();
protected:
class ExclusiveZone //封裝SynchronizationObject類的使用方式
{
SynchronizationObject *m_syncObject;
public:
ExclusiveZone( SynchronizationObject *syncObject )
: m_syncObject( syncObject )
{
m_syncObject->lock(); //構造函數中調用lock函數
}
~ExclusiveZone()
{
m_syncObject->unlock();//析構函數中調用unlock函數
}
};
virtual void setSynchronizationObject( SynchronizationObject *syncObject );
protected:
SynchronizationObject *m_syncObject;
private:
SynchronizedObject( const SynchronizedObject © );
void operator =( const SynchronizedObject © );
};
- TestResult類,與TestListener類組成觀察者模式,其中TestResult類是被觀察者,TestListener類是觀察者
-
-
-
TestResult類的runTest方法會調用每一個測試用例的run方法
-
TestResult類的關鍵代碼如下:
-
//TestResult.h
class CPPUNIT_API TestResult : protected SynchronizedObject
{
public:
TestResult( SynchronizationObject *syncObject = 0 );
virtual ~TestResult();
virtual void addListener( TestListener *listener );//添加測試用例
virtual void removeListener( TestListener *listener );//移除測試用例
virtual void addFailure( Test *test, Exception *e );//添加失敗信息
virtual void runTest( Test *test );//入口方法
protected:
void addFailure( const TestFailure &failure ); //將失敗的消息通知給所有的觀察者
protected:
typedef CppUnitDeque<TestListener *> TestListeners;
TestListeners m_listeners;//保存所有的監聽者
};
//TestResult.cpp
void TestResult::addListener( TestListener *listener )//添加測試用例
{
ExclusiveZone zone( m_syncObject );
m_listeners.push_back( listener );
}
void TestResult::removeListener ( TestListener *listener )//移除測試用例
{
ExclusiveZone zone( m_syncObject );
removeFromSequence( m_listeners, listener );
}
void TestResult::runTest( Test *test )//入口方法
{
startTestRun( test );
test->run( this );
endTestRun( test );
}
void TestResult::addFailure( Test *test, Exception *e )//用例失敗時被調用
{
TestFailure failure( test, e, false );
addFailure( failure );
}
void TestResult::addFailure( const TestFailure &failure )//將失敗的消息通知給所有的觀察者
{
ExclusiveZone zone( m_syncObject );
for ( TestListeners::iterator it = m_listeners.begin();
it != m_listeners.end();
++it )
(*it)->addFailure( failure );
}
- OutPutter相關類

-
- OutPutter是公共的基類,提供統一的接口
class CPPUNIT_API Outputter
{
public:
virtual ~Outputter() {}
virtual void write() =0; //關鍵方法
};
-
-
-
XmlOutPutter類:將執行結果按照xml模式打印出來,一般是保存到xml文件中(一般在自動化中使用)
-
CompilerOutPutter類:將執行的結果以編譯器兼容的模式打印出來,便於調試,一般不怎么使用
-
總結:就是將TestResult類中的Failure信息以不同的格式輸出
-
2. 創建測試用例的相關類
- 創建測試用例的相關類主要使用了工廠模式
- 創建的具體過程使用了宏進行簡化
- 相關類如下
- ConcretTestFixtureFactory類:用於創建測試用例對象
class TestFixtureFactory
{
public:
virtual TestFixture *makeFixture() =0; //用於創建具體測試用例的公共方法
virtual ~TestFixtureFactory() {}
};
//使用工廠方法模式
template<class TestFixtureType>
class ConcretTestFixtureFactory : public CPPUNIT_NS::TestFixtureFactory
{
TestFixture *makeFixture()
{
return new TestFixtureType(); //根據具象的類型創建實例
}
};
-
- TestSuiteBuilderContextBase類:用於將測試用例對象添加到suite中
//TestSuiteBuilderContextBase.h
class CPPUNIT_API TestSuiteBuilderContextBase
{
public:
TestSuiteBuilderContextBase( TestSuite &suite,
const TestNamer &namer,
TestFixtureFactory &factory );
virtual ~TestSuiteBuilderContextBase();
void addTest( Test *test );
protected:
TestFixture *makeTestFixture() const; //創建測試用例
TestSuite &m_suite; //用於保存測試用例的suite
const TestNamer &m_namer; //保存suite的名稱
TestFixtureFactory &m_factory; //創建測試用例的工廠
};
//TestSuiteBuilderContextBase.cpp
TestSuiteBuilderContextBase::TestSuiteBuilderContextBase(
TestSuite &suite,
const TestNamer &namer,
TestFixtureFactory &factory )
: m_suite( suite )
, m_namer( namer )
, m_factory( factory )
{//構造函數
}
TestFixture *TestSuiteBuilderContextBase::makeTestFixture() const
{
return m_factory.makeFixture();
}
void TestSuiteBuilderContextBase::addTest( Test *test ) //添加用例到suite
{
m_suite.addTest( test );
}
- TestFactory類
class CPPUNIT_API TestFactory
{
public:
virtual ~TestFactory() {}
virtual Test* makeTest() = 0;
};
- TestSuiteFactory類:此處調用的TestCaseType::suite()返回的suite就是包含測試用例的suite
template<class TestCaseType>
class TestSuiteFactory : public TestFactory
{
public:
virtual Test *makeTest()
{
return TestCaseType::suite(); //關鍵方法,該方法的實現是宏定義
}
};
-
- TestFactoryRegistry類:用於從TestFactoryRegistryList類中獲取指定名稱的TestFactoryRegistry實例
- TestFactoryRegistryList類:單例類,根據名稱保存所有的TestFactoryRegistry實例
- AutoRegisterSuite類:封裝TestSuiteFactory的注冊方式
3.框架入口類
- TestRunner類是整個CppUnit的入口類,將TestSuite類、TestResult類以及TestListener類聯合在一起,然后提供統一的入口方法,便於使用
- TestListener實例包含在TestResult實例里面
- 部分代碼如下:
//TestRunner.h
class CPPUNIT_API TestRunner
{
public:
TestRunner( );
virtual ~TestRunner();
//將需要運行的測試用例添加進來
virtual void addTest( Test *test );
//運行指定的測試用例
virtual void run( TestResult &controller, const std::string &testPath = "" );
protected:
//內部類,對suite進行了包裝
class CPPUNIT_API WrappingSuite : public TestSuite
{
public:
WrappingSuite( const std::string &name = "All Tests" );
int getChildTestCount() const;
std::string getName() const;
void run( TestResult *result );
protected:
Test *doGetChildTestAt( int index ) const;
bool hasOnlyOneTest() const;
Test *getUniqueChildTest() const;
};
protected:
WrappingSuite *m_suite;
private:
TestRunner( const TestRunner © );
void operator =( const TestRunner © );
private:
};
//TestRunner.cpp
void TestRunner::addTest( Test *test )
{
m_suite->addTest( test );
}
void TestRunner::run( TestResult &controller,
const std::string &testPath )
{
TestPath path = m_suite->resolveTestPath( testPath );
Test *testToRun = path.getChildTest();
controller.runTest( testToRun );
}
4.重要宏的解析
- CPPUNIT_TEST_SUITE_NAMED_REGISTRATION
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SimpleTest, "alltest");
//展開后如下
static CPPUNIT_NS::AutoRegisterSuite<SimpleTest> autoRegisterRegistry__12("alltest");
- CPPUNIT_TEST_SUITE、CPPUNIT_TEST以及CPPUNIT_TEST_SUITE_END宏,這三個紅必須配合使用,不能單獨使用,用於聲明需要運行的測試用例
private:
//根據typeid(SimpleTest)為名稱生成TestNamer類的實例,就是對名稱的封裝
static const CPPUNIT_NS::TestNamer &getTestNamer__()
{
static CPPUNIT_NS::TestNamer testNamer(typeid(SimpleTest));
return testNamer;
}
public:
//將測試用例添加到suite中
static void addTestsToSuite( CPPUNIT_NS::TestSuiteBuilderContextBase &baseContext )
{
CPPUNIT_NS::TestSuiteBuilderContext<SimpleTest> context(baseContext)
context.addTest(( new CPPUNIT_NS::TestCaller<SimpleTest>(context.getTestNameFor( #testMethod), &SimpleTest::testMethod, context.makeFixture())))
}
//對外接口,被TestSuiteFactory中的makeTest方法調用,返回一個完整的suite,等待被運行
static CPPUNIT_NS::TestSuite *suite()
{
const CPPUNIT_NS::TestNamer &namer = getTestNamer__();
std::auto_ptr<CPPUNIT_NS::TestSuite> suite(new CPPUNIT_NS::TestSuite(namer.getFixtureName()));
CPPUNIT_NS::ConcretTestFixtureFactory<SimpleTest> factory;
CPPUNIT_NS::TestSuiteBuilderContextBase context(*suite.get(), namer, factory );
SimpleTest::addTestsToSuite( context );
return suite.release();
}
