[vs2010 project] CppUnit快速入门
簡(jiǎn)介
測(cè)試是軟件開(kāi)發(fā)過(guò)程中極其重要的一環(huán),詳盡周密的測(cè)試能夠減少軟件BUG,提高軟件品質(zhì)。測(cè)試包括單元測(cè)試、系統(tǒng)測(cè)試等。其中單元測(cè)試是指針對(duì)軟件功能單元所作的測(cè)試,這里的功能單元可以是一個(gè)類(lèi)的屬性或者方法,測(cè)試的目的是看這些基本單元是否工作正常。由于單元測(cè)試的內(nèi)容很基礎(chǔ),因此可以看作是測(cè)試工作的第一環(huán),該項(xiàng)工作一般由開(kāi)發(fā)人員自行完成。如果條件允許,單元測(cè)試代碼的開(kāi)發(fā)應(yīng)與程序代碼的開(kāi)發(fā)同步進(jìn)行。
雖然不同程序的單元測(cè)試代碼不盡相同,但測(cè)試代碼的框架卻非常相似,于是便出現(xiàn)了一些單元測(cè)試類(lèi)庫(kù),CppUnit便是其中之一。
CppUnit是XUnit中的一員,XUnit是一個(gè)大家族,還包括JUnit和PythonUnit等。CppUnit簡(jiǎn)單實(shí)用,學(xué)習(xí)和使用起來(lái)都很方便,網(wǎng)上已有一些文章對(duì)其作介紹,但本文更著重于講解其中的基本概念和使用方法,以幫助初次接觸CppUnit的人員快速入門(mén)。
安裝
1、? 先下個(gè)最新版cppunit-1.12.1.tar.gz 解壓縮,進(jìn)入cppunit-1.12.1\src目錄,就是源代碼所在,打開(kāi)CppUnitLibraries.dsw工程,是用vc6.0寫(xiě)的,用vs2010轉(zhuǎn)換到CppUnitLibraries.sln ok,
2、? 然后依次運(yùn)行CppUnitLibraries.dsw工程下的每個(gè)項(xiàng)目,這樣做的目的是為了方面發(fā)現(xiàn)問(wèn)題和找到正確的解決方法。下面是我在運(yùn)行相應(yīng)項(xiàng)目時(shí)所提示的錯(cuò)誤以及解決辦法。未貼圖,把下述的解決辦法都做完即可,不需要查看貼圖的錯(cuò)誤提示
1)???????運(yùn)行項(xiàng)目Cppunit
解決辦法:
選擇Cppunit右鍵屬性 ->(debug)配置屬性->常規(guī)->目標(biāo)文件名:$(ProjectName)修改成cppunitd(這樣做是為保持鏈接器->常規(guī)->目標(biāo)文件名 一致);
2)???????運(yùn)行項(xiàng)目cppunit_dll
?解決辦法:
??? 選擇Cppunit右鍵屬性->(debug)配置屬性->常規(guī)->目標(biāo)文件名:$(ProjectName)修改成cppunitd_dll(這樣做是為保持庫(kù)管理器->常規(guī)->目標(biāo)文件名 一致);
3)到這里這里會(huì)發(fā)現(xiàn)其實(shí)每個(gè)項(xiàng)目的錯(cuò)誤基本上都是TargeName(xxx)與Linker的OutputFile屬性值不匹配;依次修改項(xiàng)目DllPlugInTester、DSPlugIn、TestPlugInRunner、TestRunner的Debug配置屬性(每個(gè)后面都記得加個(gè)”d”,而且?guī)旃芾砥髦袨?#xff1a;目標(biāo)文件名.xx,xx不需要修改,但庫(kù)管理器中的目標(biāo)文件名可能要修改,該成與常規(guī)—目標(biāo)文件名一致!)
4)當(dāng)然當(dāng)修改完DSPlugIn的Debug配置屬性后再運(yùn)行我們發(fā)現(xiàn)
???????????? 我們仔細(xì)觀(guān)察到底新的錯(cuò)誤是什么呢? ??????????這里修改方式就是最上面的紅色字中提到的
修改TestRunner下UserInterface\DynamicWindow\MsDevCallerListCtrl.cpp文件第67行,改成:#import"libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0")lcid("0") raw_interfaces_only named_guids
5)再次運(yùn)行DSPlugIn項(xiàng)目我們會(huì)發(fā)現(xiàn)依然有錯(cuò)誤
解決方法:
選擇項(xiàng)目->屬性->配置屬性->鏈接器->高級(jí)->無(wú)入口點(diǎn) 選擇"是(/NOENTRY)"
6)最后為了生成全面的庫(kù)文件我需要分別在Debug、release、Debug unicode、release unicode四種配置屬性中生成全部解決方案。(運(yùn)行過(guò)程中會(huì)遇到錯(cuò)誤基本上都可以從上文中找到解決方法)
7)編譯完成后,提示成功6 ?失敗 0 即安裝完畢
**************************************************************************************************詳細(xì)說(shuō)明:
解壓后,你可以看到CppUnit包含如下目錄:
config: 配置文件contrib: contribution,其他人貢獻(xiàn)的外圍代碼doc: 文檔,需要通過(guò)doxygen工具生成,也可以直接從sourceforge站點(diǎn)上下載打包好的文檔examples:示例代碼include: 頭文件lib: 存放編譯好的庫(kù)src: 源文件,以及編譯庫(kù)的工程等然后打開(kāi)src目錄下的CppUnitLibraries工程,執(zhí)行build/batch build,編譯成功的話(huà),生成的庫(kù)文件將被拷貝到lib目錄下。
你也可以根據(jù)需要選擇所需的項(xiàng)目進(jìn)行編譯,其中項(xiàng)目cppunit為靜態(tài)庫(kù),cppunit_dll為動(dòng)態(tài)庫(kù),生成的庫(kù)文件為:
cppunit.lib: 靜態(tài)庫(kù)release版cppunitd.lib: 靜態(tài)庫(kù)debug版cppunit_dll.lib: 動(dòng)態(tài)庫(kù)release版cppunitd_dll.lib:動(dòng)態(tài)庫(kù)debug版要使用CppUnit,還得設(shè)置好頭文件和庫(kù)文件路徑,以VC6為例,選擇Tools/Options/Directories,在Include files和Library files中分別添加%CppUnitPath%/include和%CppUnitPath%/lib,其中%CppUnitPath%表示CppUnit所在路徑。
做好準(zhǔn)備工作后,我們就可以編寫(xiě)自己的單元測(cè)試代碼了。需說(shuō)明的是,CppUnit所用的動(dòng)態(tài)運(yùn)行期庫(kù)均為多線(xiàn)程動(dòng)態(tài)庫(kù),因此你的單元測(cè)試程序也得使用相應(yīng)設(shè)置,否則會(huì)發(fā)生沖突。
概念
在使用之前,我們有必要認(rèn)識(shí)一下CppUnit中的主要類(lèi),當(dāng)然你也可以先看后面的例子,遇到問(wèn)題再回過(guò)頭來(lái)看這一節(jié)。
CppUnit核心內(nèi)容主要包括六個(gè)方面,
1. 測(cè)試對(duì)象(Test,TestFixture,...):用于開(kāi)發(fā)測(cè)試用例,以及對(duì)測(cè)試用例進(jìn)行組織管理。
2. 測(cè)試結(jié)果(TestResult): ? ? ? ? ? ? ? ? 處理測(cè)試用例執(zhí)行結(jié)果。TestResult與下面的TestListener采用的是觀(guān)察者模式(Observer Pattern)。
3. 測(cè)試結(jié)果監(jiān)聽(tīng)者(TestListener): ? TestListener作為T(mén)estResult的觀(guān)察者,擔(dān)任實(shí)際的結(jié)果處理角色。
4. 結(jié)果輸出(Outputter): ? ? ? ? ? ? ? ? ?將結(jié)果進(jìn)行輸出,可以制定不同的輸出格式。
5. 對(duì)象工廠(chǎng)(TestFactory): ? ? ? ? ? ? ?用于創(chuàng)建測(cè)試對(duì)象,對(duì)測(cè)試用例進(jìn)行自動(dòng)化管理。
6. 測(cè)試執(zhí)行體(TestRunner): ? ? ? ? ? 用于運(yùn)行一個(gè)測(cè)試。
以上各模塊的主要類(lèi)繼承結(jié)構(gòu)如下:
Test TestFixture TestResult TestListener _______|_________ | | | | | TestSuccessListenerTestComposite TestLeaf | | | |____________| TestResultCollector TestSuit |TestCase |TestCaller<Fixture>Outputter TestFactory TestRunner____________________|_________________ || | | TestFactoryRegistryCompilerOutputter TextOutputter XmlOutputter |TestSuiteFactory<TestCaseType>接下來(lái)再對(duì)其中一些關(guān)鍵類(lèi)作以介紹。
Test:所有測(cè)試對(duì)象的基類(lèi)。
CppUnit采用樹(shù)形結(jié)構(gòu)來(lái)組織管理測(cè)試對(duì)象(類(lèi)似于目錄樹(shù)),因此這里采用了組合設(shè)計(jì)模式(Composite Pattern),Test的兩個(gè)直接子類(lèi)TestLeaf和TestComposite分別表示“測(cè)試樹(shù)”中的葉節(jié)點(diǎn)和非葉節(jié)點(diǎn),其中TestComposite主要起組織管理的作用,就像目錄樹(shù)中的文件夾,而TestLeaf才是最終具有執(zhí)行能力的測(cè)試對(duì)象,就像目錄樹(shù)中的文件。
Test最重要的一個(gè)公共接口為:
virtual void run(TestResult *result) = 0;其作用為執(zhí)行測(cè)試對(duì)象,將結(jié)果提交給result。
在實(shí)際應(yīng)用中,我們一般不會(huì)直接使用Test、TestComposite以及TestLeaf,除非我們要重新定制某些機(jī)制。
TestFixture:用于維護(hù)一組測(cè)試用例的上下文環(huán)境。
在實(shí)際應(yīng)用中,我們經(jīng)常會(huì)開(kāi)發(fā)一組測(cè)試用例來(lái)對(duì)某個(gè)類(lèi)的接口加以測(cè)試,而這些測(cè)試用例很可能具有相同的初始化和清理代碼。為此,CppUnit引入TestFixture來(lái)實(shí)現(xiàn)這一機(jī)制。
TestFixture具有以下兩個(gè)接口,分別用于處理測(cè)試環(huán)境的初始化與清理工作:
virtual void setUp(); ? ? ? ?//初始化virtual void tearDown(); ?//清理
TestCase:測(cè)試用例,從名字上就可以看出來(lái),它便是單元測(cè)試的執(zhí)行對(duì)象。
TestCase從Test和TestFixture多繼承而來(lái),通過(guò)把Test::run制定成模板函數(shù)(Template Method)而將兩個(gè)父類(lèi)的操作融合在一起,run函數(shù)的偽定義如下:
// 偽代碼?void?TestCase::run(TestResult*?result)
{
????result->startTest(this);?//?通知result測(cè)試開(kāi)始
????if(?result->protect(this,?&TestCase::setUp)?)?//?調(diào)用setUp,初始化環(huán)境
????????result->protect(this,?&TestCase::runTest);?//?執(zhí)行runTest,即真正的測(cè)試代碼
????result->protect(this,?&TestCase::tearDown);?//?調(diào)用tearDown,清理環(huán)境
????result->endTest(this);?//?通知result測(cè)試結(jié)束
}
這里要提到的是函數(shù)runTest,它是TestCase定義的一個(gè)接口,原型如下:
virtual void runTest();用戶(hù)需從TestCase派生出子類(lèi)并實(shí)現(xiàn)runTest以開(kāi)發(fā)自己所需的測(cè)試用例。
另外還要提到的就是TestResult的protect方法,其作用是對(duì)執(zhí)行函數(shù)(實(shí)際上是函數(shù)對(duì)象)的錯(cuò)誤信息(包括斷言和異常等)進(jìn)行捕獲,從而實(shí)現(xiàn)對(duì)測(cè)試結(jié)果的統(tǒng)計(jì)。
TestSuit:測(cè)試包,按照樹(shù)形結(jié)構(gòu)管理測(cè)試用例
TestSuit是TestComposite的一個(gè)實(shí)現(xiàn),它采用vector來(lái)管理子測(cè)試對(duì)象(Test),從而形成遞歸的樹(shù)形結(jié)構(gòu)。
TestCaller:TestCase適配器(Adapter),它將成員函數(shù)轉(zhuǎn)換成測(cè)試用例
雖然我們可以從TestCase派生自己的測(cè)試類(lèi),但從TestCase類(lèi)的定義可以看出,它只能支持一個(gè)測(cè)試用例,這對(duì)于測(cè)試代碼的組織和維護(hù)很不方便,尤其是那些有共同上下文環(huán)境的一組測(cè)試。為此,CppUnit提供了TestCaller以解決這個(gè)問(wèn)題。
TestCaller是一個(gè)模板類(lèi),它以實(shí)現(xiàn)了TestFixture接口的類(lèi)為模板參數(shù),將目標(biāo)類(lèi)中某個(gè)符合runTest原型的測(cè)試方法適配成TestCase的子類(lèi)。
在實(shí)際應(yīng)用中,我們大多采用TestFixture和TestCaller相組合的方式,具體例子參見(jiàn)后文。
TestResult和TestListener:處理測(cè)試信息和結(jié)果
前面已經(jīng)提到,TestResult和TestListener采用了觀(guān)察者模式,TestResult維護(hù)一個(gè)注冊(cè)表,用于管理向其登記過(guò)的TestListener,當(dāng)TestResult收到測(cè)試對(duì)象(Test)的測(cè)試信息時(shí),再一一分發(fā)給它所管轄的TestListener。這一設(shè)計(jì)有助于實(shí)現(xiàn)對(duì)同一測(cè)試的多種處理方式。
TestFactory:測(cè)試工廠(chǎng)
這是一個(gè)輔助類(lèi),通過(guò)借助一系列宏定義讓測(cè)試用例的組織管理變得自動(dòng)化。參見(jiàn)后面的例子。
TestRunner:用于執(zhí)行測(cè)試用例
TestRunner將待執(zhí)行的測(cè)試對(duì)象管理起來(lái),然后供用戶(hù)調(diào)用。其接口為:
virtual void addTest( Test *test ); virtual void run( TestResult &controller, const std::string &testPath = "" );這也是一個(gè)輔助類(lèi),需注意的是,通過(guò)addTest添加到TestRunner中的測(cè)試對(duì)象必須是通過(guò)new動(dòng)態(tài)創(chuàng)建的,用戶(hù)不能刪除這個(gè)對(duì)象,因?yàn)門(mén)estRunner將自行管理測(cè)試對(duì)象的生命期。
CppUnit在vs2010中配置:
參考:Vs2010下使用CppUint初體驗(yàn)
http://blog.csdn.net/leer168/article/details/6708732
vs2010代碼實(shí)例:
CppUnitTest1
先讓我們看看一個(gè)簡(jiǎn)單的例子:
#include?<cppunit/TestCase.h>#include?<cppunit/TestResult.h>
#include?<cppunit/TestResultCollector.h>
#include?<cppunit/TextOutputter.h>
//?定義測(cè)試用例
class?SimpleTest?:?public?CppUnit::TestCase
{
public:
????void?runTest()?//?重載測(cè)試方法
????{
????????int?i?=?1;
????????CPPUNIT_ASSERT_EQUAL(0, i);
????}
};
int?main(int?argc,?char*?argv[])
{
????CppUnit::TestResult?r;?
????CppUnit::TestResultCollector?rc;
????r.addListener(&rc);?//?準(zhǔn)備好結(jié)果收集器?
????SimpleTest?t;
????t.run(&r);?//?運(yùn)行測(cè)試用例
????CppUnit::TextOutputter?o(&rc,?std::cout);
????o.write();?//?將結(jié)果輸出
????return?0;
}
編譯后運(yùn)行,輸出結(jié)果為: !!!FAILURES!!!
Test Results:
Run: 1 Failures: 1 Errors: 0
1) test: (F) line: 18 E:/CppUnitExamples/SimpleTest.cpp
equality assertion failed
- Expected: 1
- Actual : 0
上面的例子很簡(jiǎn)單,需說(shuō)明的是CPPUNIT_ASSERT_EQUAL宏。CppUnit定義了一組宏用于檢測(cè)錯(cuò)誤,CPPUNIT_ASSERT_EQUAL是其中之一,當(dāng)斷言失敗時(shí),CppUnit便會(huì)將錯(cuò)誤信息報(bào)告給TestResult。這些宏定義的說(shuō)明如下:
CPPUNIT_ASSERT(condition):判斷condition的值是否為真,如果為假則生成錯(cuò)誤信息。
CPPUNIT_ASSERT_MESSAGE(message, condition):與CPPUNIT_ASSERT類(lèi)似,但結(jié)果為假時(shí)報(bào)告messsage信息。
CPPUNIT_FAIL(message):直接報(bào)告messsage錯(cuò)誤信息。
CPPUNIT_ASSERT_EQUAL(expected, actual):判斷expected和actual的值是否相等,如果不等輸出錯(cuò)誤信息。
CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual):與CPPUNIT_ASSERT_EQUAL類(lèi)似,但斷言失敗時(shí)輸出message信息。
CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta):判斷expected與actual的偏差是否小于delta,用于浮點(diǎn)數(shù)比較。
CPPUNIT_ASSERT_THROW(expression, ExceptionType):判斷執(zhí)行表達(dá)式expression后是否拋出ExceptionType異常。
CPPUNIT_ASSERT_NO_THROW(expression):斷言執(zhí)行表達(dá)式expression后無(wú)異常拋出。
CppUnitTest2
接下來(lái)再看看TestFixture和TestCaller的組合使用:
#include?<cppunit/TestCase.h>#include?<cppunit/TestResult.h>
#include?<cppunit/TestResultCollector.h>
#include?<cppunit/TextOutputter.h>
#include?<cppunit/TestCaller.h>
#include?<cppunit/TestRunner.h>
//?定義測(cè)試類(lèi)
class?StringTest?:?public?CppUnit::TestFixture
{
public:
????void?setUp()?//?初始化
????{
????????m_str1?=?"Hello,?world";
????????m_str2?=?"Hi,?cppunit";
????}
????void?tearDown()?//?清理
????{
????}
????void?testSwap()?//?測(cè)試方法1
????{
????????std::string?str1?=?m_str1;
????????std::string?str2?=?m_str2;
????????m_str1.swap(m_str2);
????????
????????CPPUNIT_ASSERT(m_str1?==?str2);
????????CPPUNIT_ASSERT(m_str2?==?str1);
????}
????void?testFind()?//?測(cè)試方法2
????{
????????int?pos1?=?m_str1.find(',');
????????int?pos2?=?m_str2.rfind(',');
????????CPPUNIT_ASSERT_EQUAL(5, pos1);
????????CPPUNIT_ASSERT_EQUAL(2, pos2);
????}
protected:
????std::string?????m_str1;
????std::string?????m_str2;
};
int?main(int?argc,?char*?argv[])
{
????CppUnit::TestResult?r;?
????CppUnit::TestResultCollector?rc;
????r.addListener(&rc);?//?準(zhǔn)備好結(jié)果收集器?
????CppUnit::TestRunner?runner;?//?定義執(zhí)行實(shí)體
????runner.addTest(new?CppUnit::TestCaller<StringTest>("testSwap",?&StringTest::testSwap));?//?構(gòu)建測(cè)試用例1
????runner.addTest(new?CppUnit::TestCaller<StringTest>("testFind",?&StringTest::testFind));?//?構(gòu)建測(cè)試用例2
????runner.run(r);?//?運(yùn)行測(cè)試
????CppUnit::TextOutputter?o(&rc,?std::cout);
????o.write();?//?將結(jié)果輸出
????return?rc.wasSuccessful()???0?:?-1;
}
編譯后運(yùn)行結(jié)果為: OK (2 tests)
CppUnitTest3
上面的代碼從功能上講沒(méi)有什么問(wèn)題,但編寫(xiě)起來(lái)太繁瑣了,為此,我們可以借助CppUnit定義的一套輔助宏,將測(cè)試用例的定義和注冊(cè)變得自動(dòng)化。上面的代碼改造后如下:
#include?<cppunit/TestResult.h>#include?<cppunit/TestResultCollector.h>
#include?<cppunit/TextOutputter.h>
#include?<cppunit/TestRunner.h>
#include?<cppunit/extensions/HelperMacros.h>
//?定義測(cè)試類(lèi)
class?StringTest?:?public?CppUnit::TestFixture
{
????CPPUNIT_TEST_SUITE(StringTest);??//?定義測(cè)試包
????CPPUNIT_TEST(testSwap);??//?添加測(cè)試用例1
????CPPUNIT_TEST(testFind);??//?添加測(cè)試用例2
????CPPUNIT_TEST_SUITE_END();??//?結(jié)束測(cè)試包定義
????
public:
????void?setUp()?//?初始化
????{
????????m_str1?=?"Hello,?world";
????????m_str2?=?"Hi,?cppunit";
????}
????void?tearDown()?//?清理
????{
????}
????void?testSwap()?//?測(cè)試方法1
????{
????????std::string?str1?=?m_str1;
????????std::string?str2?=?m_str2;
????????m_str1.swap(m_str2);
????????
????????CPPUNIT_ASSERT(m_str1?==?str2);
????????CPPUNIT_ASSERT(m_str2?==?str1);
????}
????void?testFind()?//?測(cè)試方法2
????{
????????int?pos1?=?m_str1.find(',');
????????int?pos2?=?m_str2.rfind(',');
????????CPPUNIT_ASSERT_EQUAL(5, pos1);
????????CPPUNIT_ASSERT_EQUAL(2, pos2);
????}
protected:
????std::string?????m_str1;
????std::string?????m_str2;
};
CPPUNIT_TEST_SUITE_REGISTRATION(StringTest);?//?自動(dòng)注冊(cè)測(cè)試包
int?main(int?argc,?char*?argv[])
{
????CppUnit::TestResult?r;?
????CppUnit::TestResultCollector?rc;
????r.addListener(&rc);?//?準(zhǔn)備好結(jié)果收集器?
????CppUnit::TestRunner?runner;?//?定義執(zhí)行實(shí)體
????runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
????runner.run(r);?//?運(yùn)行測(cè)試
????CppUnit::TextOutputter?o(&rc,?std::cout);
????o.write();?//?將結(jié)果輸出
????return?rc.wasSuccessful()???0?:?-1;
}
CppUnit的簡(jiǎn)單介紹就到此,相信你已經(jīng)了解了其中的基本概念,也能夠開(kāi)發(fā)單元測(cè)試代碼了。
其它CppUnit還包括其它一些輔助模塊,比如基于MFC的圖形化測(cè)試界面,下面這篇文章對(duì)此有所介紹:
???? CppUnit測(cè)試框架入門(mén)?
CppUnit使用了很多設(shè)計(jì)模式,整體構(gòu)架還算清晰合理,源碼也比較簡(jiǎn)單易懂,這對(duì)于學(xué)習(xí)設(shè)計(jì)模式是一個(gè)不錯(cuò)的選擇。網(wǎng)上已有這樣的一些資料:
???? CppUnit源碼解讀 ????? CppUnit代碼簡(jiǎn)介 - 第一部分,核心類(lèi)(freefalcon于2006-05-22)
總結(jié)
以上是生活随笔為你收集整理的[vs2010 project] CppUnit快速入门的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数字孪生CIM智慧城市BIM,城市cim
- 下一篇: 百行代码,轻松实现机器人语音对话