CPPUnit是由JUnit轉化而來的一種C++程式測試架構,最初的作者是Michael Feathers
CodeUnitTestFirst is not a testing technique, it's a design technique.
先要瞭解一下JUnit/xUnit架構的實現思想
典型的單元測試可以描述為:
確保方法接受預期範圍內的輸入,並且對每個測試輸入返回預期的結果
單元測試 :單元測試測的是獨立的一個工作單元。在Java應用程式中,獨立的一個工作單元常常指的是一個方法(但並不總是如此)。作為對比,整合測試和接收測試則檢查多個組件如何互動。一個工作單元是一項任務,它不依賴於其他任何任務的完成。
單元測試所關注的常常是方法是否滿足API契約。就如同人們同意在某種條件下交換特定的貨物或者服務所寫下的契約,API契約被看作是方法介面的正式協定。方法要求調用者提供特定的值或對象,並(作為交換)會返回特定的值或對象。如果契約不能滿足,那麼方法就拋出異常來表明契約沒有被遵守。如果一個方法的行為同預期不符,那麼我們說這個方法破壞了契約。
API契約:對應用編程介面(API)的一種看法,把它看作是調用者和被調用者之間的正式協定。單元測試常常可以通過證實期待的結果來協助定義API契約。API契約的說法來自伴隨Eiffel程式設計語言而流行的Design by Contract實踐(http://archive.eiffel.com/doc/manuals/technology/contract)。
哪些內容值得測試Right-BICEP
Right--Are the results right? 結果是否正確
B--Are all the boundary conditions CORRECT? 所有邊界條件是否正確
I--Can you check inverse relationships? 是否檢查了反向關係
C--Can you cross-check results using other means? 交叉檢查結果是否正確.(換種演算法來檢查結果是否一致)E--Can you force error conditions to happen? 強制錯誤條件出現時結果是否正確
P--Are performance characteristics within bounds? 是否滿足效能要求
CORRECT邊界條件
C--Conformance: Does the value conform to an expected format?
O--Ordering:Is the set of values ordered or unordered as appropriate?
R--Range:Is the value within reasonable minimum and maximum values?
R--Reference: Does the code reference anything external that isn't under direct control of the code itself?
E--Existence:Does the value exist (e.g., is non-null, nonzero,present in a set, etc.)?
C--Cardinality: Are there exactly enough values?
T--Time (absolute and relative): Is everything happening in order? At the right time? In time?
一般來說,下面的情形往往容易發生bug
--Totally bogus or inconsistent input values, such as a file name of "!*W:Xn&Gi/w>g/h#WQ@".
--Badly formatted data, such as an e-mail address without a top-level domain ("fred@foobar.").
--Empty or missing values (such as 0, 0:0, "", or null).
--Values far in excess of reasonable expectations, such as a person's age of 10,000 years.
--Duplicates in lists that shouldn't have duplicates.
--Ordered lists that aren't, and vice-versa. Try handing a pre-sorted list to a sort algorithm, for instance--or even a reverse-sorted list.
--Things that arrive out of order, or happen out of expected order, such as trying to print a document before logging in, for instance.
--Useful Links:
http://sourceforge.net/projects/cppunit/
http://cppunit.sourceforge.net/cppunit-wiki/FrontPage
http://www.junit.org/
http://www.mockobjects.com/
--Installation on Linux
1. Download cppunit-1.10.2 package from cppunit site and extract it
http://cppunit.sourceforge.net
2. Download autoconf 2.2.4
http://ftp.man.poznan.pl/pub/gnu/gnu/autoconf/
3. Install autoconf
./configure
./make
./make install
4. Install cppunit
./configure CXX=/usr/bin/g++296 CXXFLAGS=-O2
./make
./make install
./make clean
5. Verify installation
ls /usr/local/lib/libcppunit*
--Installation on Windows
(summarized by hongshengyi)
1. 編譯cppunit目錄/src/cppunit/CppUnitLibraries.dsw中的cppunit_dll項目,release版產生cppunit_dll.lib和cppunit_dll.dll,debug版產生cppunitd_dll.lib和cppunitd_dll.dll。這是CPPUnit基本類庫。
2. 編譯cppunit目錄/src/cppunit/CppUnitLibraries.dsw中的TestRunner項目。release版產生TestRunner.lib和TestRunner.dll,debug版產生TestRunnerd.lib和TestRunnerd.dll。這是使用MFC的圖形化介面的類庫。
3. 把所有lib檔案放置到CommonFiles/Lib目錄下面。
或者 在VC中工具-》選擇-》目錄-》Library Files中將cppunit中的lib目錄放進來
4. 把dll檔案放到相應的debug和release可執行目錄下面。
5. 在VC中工具-》選擇-》目錄-》Include Files中將cppunit中的include目錄放進來
6.vc中工程-》設定-》link 放入cppunitd_dll.lib
在Projects/Settings.../C++/C++ Language頁選中Enable RTTI。
在Projects/Settings.../C++/Code Generation頁選擇Use run-time library中的內容:
Release版, 選擇"Mulithreaded DLL".
Debug版, 選擇 "Debug Multihreaded DLL".
7.可以引入宏AddingUnitTestMethod.dsm,可以方便產生測試架構
8.寫完待測試和測試類別後,還要寫個main函數
--Example
----------------------Hello world of Cpp unit------------------
(http://pantras.free.fr/articles/helloworld.html)
#include <iostream>
#include <cppunit/TestRunner.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
class Test : public CPPUNIT_NS::TestCase
{
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(testHelloWorld);
CPPUNIT_TEST_SUITE_END();
public:
void setUp(void) {}
void tearDown(void) {}
protected:
void testHelloWorld(void) { std::cout << "Hello, world!" << std::endl; }
};
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
int main( int argc, char **argv )
{
// 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 );
return result.wasSuccessful() ? 0 : 1;
}
Complie:
/usr/bin/g++296 -O2 -g -o helloworld helloworld.cpp /
-I. -I./h -I/usr/include -I/usr/local/include /
-L/usr/lib -L/usr/local/lib -ldl -lm -lpthread -lcppunit
--Key points:
1.先寫測試代碼,然後編寫符合測試的代碼。至少做到完成部分代碼後,完成對應的測試代碼;
2.測試代碼不需要覆蓋所有的細節,但應該對所有主要的功能和可能出錯的地方有相應的測試案例;
3.發現 bug,首先編寫對應的測試案例,然後進行調試;
4.不斷總結出現 bug 的原因,對其他代碼編寫相應測試案例;
5.每次編寫完成代碼,運行所有以前的測試案例,驗證對以前代碼影響,把這種影響儘早消除;
6.不斷維護測試代碼,保證代碼變動後通過所有測試;
--Main steps
1. Initialize test fixture
2. Initialize testcase : setUp
3. Execute test case: assert
4. Release testcase: tearDown
5. Release test fixture
提供的斷言:
CPPUNIT_ASSERT(condition) // 確信condition為真
CPPUNIT_ASSERT_MESSAGE(message, condition) // 當condition為假時失敗, 並列印message
CPPUNIT_FAIL(message) // 當前測試失敗, 並列印message
CPPUNIT_ASSERT_EQUAL(expected, actual) // 確信兩者相等
CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual) // 失敗的同時列印message
CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta) // 當expected和actual之間差大於delta時失敗
運行方式:
CpUnit::TextUi::TestRunner // 文本方式的TestRunner
CppUnit::QtUi::TestRunner // QT方式的TestRunner
CppUnit::MfcUi::TestRunner // MFC方式的TestRunner
(Thanks liqun and hongshengyi's article)