測試驅動開發的原則:
Ø 先寫測試代碼,然後編寫符合測試的代碼。至少做到完成部分代碼後,完成對應的測試代碼;
Ø 測試代碼不需要覆蓋所有的細節,但應該對所有主要的功能和可能出錯的地方有相應的測試用例;
Ø 發現 bug,首先編寫對應的測試用例,然後進行調試;
Ø 不斷總結出現 bug 的原因,對其他代碼編寫相應測試用例;
Ø 每次編寫完成代碼,運行所有以前的測試用例,驗證對以前代碼影響,把這種影響儘早消除;
Ø 不斷維護測試代碼,保證代碼變動後通過所有測試;
Ø 在編碼前:他可以強迫你對需求進行詳細的分析。
Ø 在編碼時:他可以使你對over coding保持警覺。
Ø 在重構時:可以確保新的設計能夠兼容舊版本的功能。
Ø 在團隊開發時:可以確保自己的單元是無誤的。
CppUnit的原理
在 CppUnit 中,一個或一組測試用例的測試對象被稱爲 Fixture(設施,下文爲方便理解儘量使用英文名稱)。Fixture 就是被測試的目標,可能是一個對象或者一組相關的對象,甚至一個函數。
有了被測試的 fixture,就可以對這個 fixture 的某個功能、某個可能出錯的流程編寫測試代碼,這樣對某個方面完整的測試被稱爲TestCase(測試用例)。通常寫一個 TestCase 的步驟包括:
1. 對 fixture 進行初始化,及其他初始化操作,比如:生成一組被測試的對象,初始化值;
2. 按照要測試的某個功能或者某個流程對 fixture 進行操作;
3. 驗證結果是否正確;
4. 對 fixture 的及其他的資源釋放等清理工作。
對 fixture 的多個測試用例,通常(1)(4)部分代碼都是相似的,CppUnit 在很多地方引入了 setUp 和 tearDown 虛函數。可以在 setUp 函數裏完成(1)初始化代碼,而在 tearDown 函數中完成(4)代碼。具體測試用例函數中只需要完成(2)(3)部分代碼即可,運行時 CppUnit 會自動爲每個測試用例函數運行 setUp,之後運行 tearDown,這樣測試用例之間就沒有交叉影響。
撰寫TestCase必須注意以下幾點:
Ø 可以自動執行,不用人手操作。
Ø 自動返回測試結果。
Ø 絕對的獨立,不能與其他TestCase有任何聯繫。就算測試同一個函數的不同功能也需要分開。每個TestCase可以說是一個孤島。
對 fixture 的所有測試用例可以被封裝在一個 CppUnit::TestFixture 的子類(命名慣例是[ClassName]Test)中。然後定義這個fixture 的 setUp 和 tearDown 函數,爲每個測試用例定義一個測試函數(命名慣例是 testXXX)。下面是個簡單的例子:
class MathTest : public CppUnit::TestFixture {
protected:
int m_value1, m_value2;
public:
MathTest() {}
// 初始化函數
void setUp () {
m_value1 = 2;
m_value2 = 3;
}
// 測試加法的測試函數
void testAdd () {
// 步驟(2),對 fixture 進行操作
int result = m_value1 + m_value2;
// 步驟(3),驗證結果是否爭取
CPPUNIT_ASSERT( result == 5 );
}
// 沒有什麼清理工作沒有定義 tearDown.
}
在測試函數中對執行結果的驗證成功或者失敗直接反應這個測試用例的成功和失敗。CppUnit 提供了多種驗證成功失敗的方式:
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時失敗
要把對 fixture 的一個測試函數轉變成一個測試用例,需要生成一個 CppUnit::TestCaller 對象。而最終運行整個應用程序的測試代碼的時候,可能需要同時運行對一個 fixture 的多個測試函數,甚至多個 fixture 的測試用例。CppUnit 中把這種同時運行的測試案例的集合稱爲 TestSuite。而 TestRunner 則運行測試用例或者 TestSuite,具體管理所有測試用例的生命週期。目前提供了 3 類TestRunner,包括:
Ø CppUnit::TextUi::TestRunner // 文本方式的TestRunner
Ø CppUnit::QtUi::TestRunner // QT方式的TestRunner
Ø CppUnit::MfcUi::TestRunner // MFC方式的TestRunner
下面是一個TestRunner的例子:
CppUnit::TextUi::TestRunner runner;
CppUnit::TestSuite *suite= new CppUnit::TestSuite();
// 添加一個測試用例
suite->addTest(new CppUnit::TestCaller<MathTest> (
"testAdd", testAdd));
// 指定運行TestSuite
runner.addTest( suite );
// 開始運行, 自動顯示測試進度和測試結果