1.QTS單元測試框架介紹
目前QTS項目采用C/C++語言,而CppUnit就是xUnit家族中的一員,它是一個專門面向C++的單元測試框架。因此,QTS采用CppUnit測試框架是比較理想的選擇。
CppUnit按照層次來管理測試,最底層的就是TestCase,當有了幾個TestCase以后,可以將它們組織成TestFixture。在TestFixture中,可以建立被測試的類的實例,并編寫TestCase對類實例進行測試,多個TestFixture可以通過TestSuite來對測試進行管理。
通過派生TestFixture類來設計某個類或某組相關功能的單元測試,Fixture定義公共函數setUp()初始化每個成員變量,tearDown()來釋放setUp中使用的資源。在每個測試中,CPPUNIT_ASSERT(bool)來判斷某個函數和表達式的正確性,在派生類的聲明中,通過CPPUNIT_TEST來增加對應的測試函數,通過CPPUNIT_TEST_SUITE和CPPUNIT_TEST_SUITE_END來分裝所有的測試函數,規定這些測試函數執行的順序。
?2.QTS單元測試框架搭建
2.1CppUnit 介紹
A、CppUnit源代碼組成
CppUnit測試框架的源代碼可以到 http://sourceforge.net/projects/cppunit/ 上下載。下載解壓后,你將看到如下文件夾:
主要的文件夾有:
· doc: CppUnit的說明文檔。另外,代碼的根目錄,還有三個說明文檔,分別是INSTALL,INSTALL-unix,INSTALL-WIN32.txt。
· examples: CpppUnit提供的例子,也是對CppUnit自身的測試,通過它可以學習如何使用CppUnit測試框架進行開發。
· include: CppUnit頭文件。
· src: CppUnit源代碼目錄。
B、初識CppUnit測試環境
解壓源代碼包后, CppUnit結構如下:
????1、進入example文件夾,用VC打開examples.dsw。我們先來看看CppUnit自帶的測試例子。這些例子都是針對CppUnit自身的單元測試集,一方面這是CppUnit作者開發CppUnit框架過程中寫的測試用例,另一方面,我們可以通過這些例子來學習如何在我們自己的工程中添加測試用例。
????2、將CppUnitTestApp工程設為Active Project(Win32 Debug),編譯后運行,則可以看到CppUnit的基于GUI方式進行單元測試TestRunner的界面。點擊“Run”,將會看到如圖二所示界面:
?
這是一個針對CppUnit的單元測試結果,它表明剛才我們做了11個測試,全部通過。
點擊“Browse”,我們還可以選擇想要進行的單元測試,如圖三:
2.2 CppUnit單元測試環境搭建
第一步:編譯CppUnit 靜態庫文件*.lib和動態庫文件*.dll:
- CppUnit的lib和dll
?CppUnit為我們提供了兩套框架庫,一個為靜態的lib,一個為動態的dll。
??cppunit project:靜態lib
? cppunit_dll project:動態dll和lib
在開發中我們可以根據實際情況作出選擇。進入src文件夾,打開CppUnitLibraries.dsw。分別編譯這兩個project,輸出位置均為lib文件夾。
在開發中我們可以根據實際情況作出選擇。進入src文件夾,打開CppUnitLibraries.dsw。在菜單上選擇Build->Batch Build..->Rebuild All,輸出位置均為lib文件夾。
為了方便開發,我們把這些編譯出來的lib和dll拷貝到我們自己建立的一個文件夾中(當然你也可以不這么做),例如F:\Mytest\lib\,同時我們也把CppUnit源代碼中include文件夾copy到我們自己的include文件夾下。???
第二步:建立基于對話框的工程
打開VC,在File菜單項下選擇New,建立基于dialog的工程。工程名Project name、存放位置Location可以自己決定,其他選項如下:
按OK確認后,進入如下界面。選擇Dialog based選項,按Finish按鈕后,一個空的基于對話框的工程就建立起來了。
第三步:屏蔽工程對話框
在工程CouterTest.cpp文件中(本指南中為該文件名,實際學習時根據自己的工程文件名而變),找到BOOL CCounterTestApp::InitInstance()方法,將如下附帶代碼注釋掉:也就是代碼中帶*的部分
BOOL CCounterTestApp::InitInstance()
{AfxEnableControlContainer();// Standard initialization// If you are not using these features and wish to reduce the size// of your final executable, you should remove from the following// the specific initialization routines you do not need.#ifdef _AFXDLLEnable3dControls(); // Call this when using MFC in a shared DLL
#elseEnable3dControlsStatic(); // Call this when linking to MFC statically
#endif/*CCounterTestDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is// dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is// dismissed with Cancel}*/// Since the dialog has been closed, return FALSE so that we exit the// application, rather than start the application's message pump.return FALSE;
}
由于我們希望這個Project運行后顯示的是圖2這樣的CppUnit自帶的界面,所以我們需要在Instance()中屏蔽掉原有的對話框(藍色部分注釋掉),代之以CppUnit的GUI。
第四步:實現CppUnit測試執行器,并將測試套添加到測試執行器中。
A、在BOOL CCounterTestApp::InitInstance()中,添加如下附加注釋的代碼:
BOOL CCounterTestApp::InitInstance()
{AfxEnableControlContainer();// Standard initialization// If you are not using these features and wish to reduce the size// of your final executable, you should remove from the following// the specific initialization routines you do not need.#ifdef _AFXDLLEnable3dControls(); // Call this when using MFC in a shared DLL
#elseEnable3dControlsStatic(); // Call this when linking to MFC statically
#endif//添加CppUnit的MFC類型的測試執行器CppUnit::MfcUi::TestRunner runner; //為被測試類(這里是CCounter)定義一個測試工廠(這里取名叫CounterTest):CppUnit::TestFactoryRegistry ®istry
= CppUnit::TestFactoryRegistry::getRegistry("CounterTest");//并將工廠添加到測試執行器中
runner.addTest( registry.makeTest() );//運行執行器,顯示執行器GUI界面runner.run(); /*CCounterTestDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is// dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is// dismissed with Cancel}*/// Since the dialog has been closed, return FALSE so that we exit the// application, rather than start the application's message pump.return FALSE;
}
B、由于在BOOL CCounterTestApp::InitInstance()中引用了CppUnit的類,所以在文件開始處要添加如下頭文件:
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/mfc/TestRunner.h>
第五步:添加被測對象CCounter。
將被測對象所在文件(CounterMod.h和CounterMod.cpp) 添加到工程中:
第六步:在工程中為被測對象CCounter編寫測試類文件MyTest(可以自定義文件名):
按照下面示圖加入測試類的*.h文件和*.cpp文件:
MyTest.h中的代碼如下:
#include "cppunit/extensions/HelperMacros.h"class IsCodeLineTest : public CppUnit::TestFixture {// 聲明一個TestSuiteCPPUNIT_TEST_SUITE( IsCodeLineTest);// 添加測試用例到TestSuite, 定義新的測試用例需要在這兒聲明一下CPPUNIT_TEST( Test1 );// TestSuite聲明完成CPPUNIT_TEST_SUITE_END();public:// 定義測試用例void Test1 ();};
MyTest.cpp中的代碼如下(注意頭文件要做相應的修改):
#include "stdafx.h"#include "MyTest.h"
#include "CounterMod.h"// 把這個TestSuite注冊到名字為"CounterTest"的工廠中
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( IsCodeLineTest,"CounterTest" );#define RET_OK 0
#define RET_FAIL 1void IsCodeLineTest::Test1()
{//定義輸入參數int bIsComment;CString szFileLine;//定義期望輸出int iOkReturn;int iOkIsComment;//定義測試實際輸出int iResult;CCounter m_counter;//用例輸入szFileLine = "int a";bIsComment = false;//期望輸出iOkReturn = RET_OK;iOkIsComment = false;//驅動被測函數iResult = m_counter.IsCodeLine(szFileLine,bIsComment);//結果比較CPPUNIT_ASSERT_EQUAL(iOkReturn,iResult);CPPUNIT_ASSERT_EQUAL(iOkIsComment,bIsComment);
}
第七步:加入CppUnit 庫文件:
把CppUnit相關的lib文件和dll文件(cppunitd.lib,cppunitd_dll.lib,testrunnerd.lib)加入到工程中:
第八步:設置頭文件和lib庫文件路徑、打開RTTI開關、給dLL庫設置環境變量:
- 在VC的tools/options/directories/include files和library files中設置CppUnit include文件路徑和lib文件路徑:
在你的VC project中打開RTTI開關。具體位置Project Settings/C++/C++ Language:
C、為TestRunnerd.dll設置環境變量
TestRunnerd.dll為我們提供了基于GUI的測試環境。為了讓我們的測試程序能正確的調用它,需要把TestRunnerd.dll拷貝到你的工程路徑下。或者最簡單的方法是在操作系統的環境變量Path中添TestRunnerd.dll的路徑,這樣是最省事的。
第九步:編譯執行。編譯連接成功后,運行測試,出現下面的界面,表示測試用例Test1運行成功.