TDD相信很多人都瞭解,它是敏捷式軟體開發 (Agile Software Development)中不可缺少的一個環節,能使你編寫出更高品質,使你更放心的代碼(當然,並不是指使用了TDD之後,就不會出現bug,呵呵,你懂的……)
那麼,要使用TDD就要有一個測試架構,所以,在C++環境中,一個簡單使用的單元測試架構CppUnit會為你幫上不少的忙
我對CppUnit的學習是從下面的文章中學習到的:
MFC UI介面的:http://www.vckbase.com/document/viewdoc/?id=1762
一個普通Cmd介面的:http://www.cs.nmsu.edu/~jeffery/courses/371/cppunit/cppunit_cookbook.html
下面是這兩天來對CppUnit的學習記錄:
首先,在CppUnit中的架構是:
TestRunner->TestSuite->TestCase
//這裡說的名字是對應著CppUnit中的類的,但並是代表在其對應的層次上就只可以使用這個類,對應的層次上有各種其他可能適合大家的類,相信這三個會是使用的比較多的(還有一個是TestFixture吧,也十分常用)
TestRunner主要充當調用者的角色(從名字上可以理解到吧)
TestSuite主要充當著一個封包的角色,即將你所有的test cases都包在一起(不單止是test cases,它同樣可以包含一個suite的)
TestCase主要充當一個測試案例,如你要測試:
void testFalse()
{
CPPUNIT_ASSERT(1!=2);
}
這個testFalse就是一個case,Suite就是包含大量這些case的合集(可能還有更多更深的含義,在下並無深入學習,就這是我自己的理解)
下面是一個簡單的例子:
//這裡使用到許多HelperMarco.h中的宏,通常如果只是使用CppUnit的話,沒有必要這道這些Marco實現是怎樣的,知道怎麼用就可以了,這是我的理解
這裡是模仿cppunit_cook_book中的例子:
首先需要一對C++檔案:ComplexTest.h/cpp //顧名思義就是對Complex類的測試檔案
#ifndef_COMPLEX_NUM_TEST_H<br />#define_COMPLEX_NUM_TEST_H<br />#include <string><br />#include "cppunit/TestSuite.h"<br />#include "cppunit/ui/text/TestRunner.h"<br />#include "cppunit/extensions/HelperMacros.h"<br />#include "cppunit/extensions/TestFactoryRegistry.h"<br />#include "Complex.h"<br />using std::string;<br />class ComplerTest : public CppUnit::TestFixture<br />{<br />CPPUNIT_TEST_SUITE(ComplerTest);// 首先聲明一個Suite<br />CPPUNIT_TEST(testEquality);// 往Suite裡面加入Cases<br />CPPUNIT_TEST(testAddition);<br />CPPUNIT_TEST(testFalse);<br />CPPUNIT_TEST_SUITE_END();// 聲明Suite結束<br />// 上面幾個宏非常簡單就做了許許多多的事,要瞭解實際過程是怎樣,參考cppunit_cook_book的文章吧<br />public:<br />// CppUnit會調用setUp虛函數對資料進行初始化<br />// virtual<br />void setUp()<br />{<br />m_p10_1 = new Complex(10, 1);<br />m_p10_2 = new Complex(10, 2);<br />m_p11_1 = new Complex(11, 1);<br />}<br />// CppUnit會調用tearDown清理立即<br />// virtual<br />void tearDown()<br />{<br />delete m_p10_1;<br />delete m_p10_2;<br />delete m_p11_1;<br />}<br />//我們的測試案例<br />void testEquality()<br />{<br />CPPUNIT_ASSERT(*m_p10_1 == *m_p10_1);<br />CPPUNIT_ASSERT(*m_p10_1 != *m_p10_1);<br />}<br />void testAddition()<br />{<br />CPPUNIT_ASSERT(*m_p10_1 + *m_p10_2 == Complex(20, 3));<br />CPPUNIT_ASSERT(*m_p10_1 + *m_p11_1 == Complex(20, 3));<br />}<br />void testFalse()<br />{<br />CPPUNIT_ASSERT( 1 == 2);<br />}<br />private:<br />Complex *m_p10_1;<br />Complex *m_p10_2;<br />Complex *m_p11_1;<br />};<br />#endif//_COMPLEX_NUM_TEST_H
#include "ComplexNumTest.h"<br />// 將前面聲明的Suite註冊到CppUnit裡面,我們將會在TestRunner裡面調用這個已經註冊在內部的Suite<br />/*<br /> 這裡有一個很嚴重的問題,一定要將這個實現放在一個.cpp檔案中<br /> 放到.h檔案中會被多次註冊到CppUnit中(視你引用.h檔案的次)<br /> 當你run的時候,測試案例就會被多次調用的了,所以要十分主意<br />*/<br />CPPUNIT_TEST_SUITE_REGISTRATION(ComplerTest);
然後就需要在我們的main函數裡面調用我們的測試案例了
#include "ComplexNumTest.h"<br />int main()<br />{<br />CppUnit::TextUi::TestRunner runner;//聲明一個使用TextUi的TestRunner<br />//擷取我們剛才註冊到CppUnit裡面的測試案例<br />CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry();<br />runner.addTest(registry.makeTest());<br />runner.run();<br />return 0;<br />}
由於在這個時候運行肯定會錯的,所以不斷進行修改,讓編譯通過,所有的CPPUNIT_ASSERT()通過就是我們的任務了,這裡就開始要體現到TDD的威力了,而CppUnit也已經為我們打下了堅實的基礎了!!!它讓我們知道我們哪裡錯了!要做哪些事情,要定義一些怎樣的介面等,不單止為我們日後維護提供保障,對我們code層面上的設計都是十分有協助的,這些都是需要我們不斷從編碼時間中得到的!!!!
對於Complex的實現這裡就不貼出代碼了,自己實現吧,哈哈!