1.常用的C++單元測試框架
測試驅動開發(TDD)已經是一種非常流行的開發方式了,在Java和.Net中都提供了非常好的單元測試框架,最近研究C++下面的單元測試,發現其實在C++中還是有很多選擇:
CPPUnit:著名的XUnit系列產品之一,熟悉JUnit、NUnit的開發人員可以很快上手。
CXXTest:需要進行預處理,需要安裝Perl或Python。
Boost Test:功能強大,提供了自動注冊和手動注冊兩種方式,更重要的是來自千錘百煉的Boost庫。
Google Test:Google在去年開源的測試框架,據說其內部上千個項目均采用該框架進行單元測試。
2.Boost Test起步
先來看一個簡單的Boost Test示例(來自Boost文檔):
測試初步
1#define BOOST_TEST_MODULE example
2
3#include <boost/test/unit_test.hpp>
4
5
6
7int add(int i, int j);
8
9
10
11BOOST_AUTO_TEST_SUITE(minimal_test)
12
13
14
15BOOST_AUTO_TEST_CASE(my_test)
16
17{
18
19 BOOST_CHECK(add(2, 2) == 5);
20
21 BOOST_REQUIRE(add(2, 2) == 4);
22
23 if(add(2, 2) != 4)
24
25 BOOST_ERROR("oops!");
26
27 if(add(2, 2) != 4)
28
29 BOOST_FAIL("oops!");
30
31 if(add(2, 2) != 4)
32
33 throw "oops!";
34
35 BOOST_CHECK_MESSAGE(add(2, 2) == 4, "add(..) result: " << add(2, 2));
36
37 BOOST_CHECK_EQUAL(add(2, 2), 4);
38
39}
40
41
42
43BOOST_AUTO_TEST_SUITE_END()
44
45
首先,需要定義#define BOOST_TEST_MODULE example或者#define BOOST_AUTO_TEST_MAIN,否則測試模塊初始化函數需要手動實現;然後宏“BOOST_AUTO_TEST_SUITE(minimal_test)”將創建一個名為minimal_test的測試套件,並將其加入到測試模塊中。宏“BOOST_AUTO_TEST_CASE(my_test)”將創建一個名為“my_test”的測試用例,並將其加入到測試套件minimal_test中。
關於測試模塊常犯的一個錯誤是在不同的測試文件中定義不同的“#define BOOST_TEST_MODULE example”,最後將導致“multiple definition of ‘init_unit_test_suite(int, char**)’”錯誤,原因是在一個測試程序中只允許存在一個定義一個測試模塊。測試模塊中存在一個主測試套件,所有未明確包含到測試套件中的測試用例將被包含到主測試套件中。
下面是具體的測試過程了,在程序中展示了七種不同的方式來對函數add進行測試:
BOOST_CHECK:這種方法將檢查到的錯誤保存起來,測試結束時由測試框架自動顯示;
BOOST_REQUIRE:同樣是檢查錯誤,與BOOST_CHECK不同之處在於如果發生錯誤時將拋出一個異常,因此後續的測試將不會繼續進行;
BOOST_ERROR:可以用來對錯誤進行獨立描述,測試結束時由測試框架自動顯示;
BOOST_FAIL:同樣可以用來對錯誤進行獨立描述,調用時將拋出一個異常;
拋出異常方式:測試框架將捕獲該異常並打印出錯誤信息;
BOOST_CHECK_MESSAGE:與BOOST_CHECK類似,可以在第二個參數中加入錯誤描述信息;
BOOST_CHECK_EQUAL:與BOOST_CHECKL類似,用來檢查兩個參數是否相等。
3.單參數測試用例
有時候需要對一個測試用例使用不同的參數運行測試,前面提到的BOOST_AUTO_TEST_CASE不能滿足需求,此時需要對測試用例進行手動注冊(來自Boost文檔):
單參數測試用例
#include <boost/test/included/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
using namespace boost::unit_test;
void free_test_function( int i )
{
BOOST_CHECK( i < 4 /* test assertion */ );
}
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
int params[] = { 1, 2, 3, 4, 5 };
framework::master_test_suite().
add( BOOST_PARAM_TEST_CASE( &free_test_function, params, params+5 ) );
return 0;
}
示例代碼將會分別以參數1、2、3、4、5運行測試free_test_function五次。手動注冊需要定義測試函數,此處為free_test_function,然後定義init_unit_test_suite函數,並在該函數中將測試用例加入到主測試套件中。BOOST_PARAM_TEST_CASE有三個參數:第一個參數為測試函數指針,第二個與第三個為輸入參數迭代器。
4.夾具(Fixture)
如果在多個測試用例中需要使用數據庫連接,這時候要用到夾具來自動執行安裝、清理過程。Boost Test采用RAII技術來實現夾具:
1struct <fixture-name>{
2
3 <fixture-name>(); // 安裝
4
5 ~<fixture-name>(); // 拆卸
6
7};
自定義一個夾具非常簡單,只需要定義一個類,分別在類的構造函數和析構函數中進行安裝和拆卸。另外,類的公有成員在測試用例中可以直接引用而不需要加上對象名作為前綴。例如:
夾具示例
1#define BOOST_TEST_MODULE example
2
3#include <boost/test/included/unit_test.hpp>
4
5
6
7struct F
8
9{
10
11 F() : i(0)
12
13 {
14
15 //std::cout << ("創建夾具") << std::endl;
16
17 }
18
19 ~F()
20
21 {
22
23 //std::cout << "銷毀夾具" << std::endl;
24
25 }
26
27
28
29 int i;
30
31};
32
33
34
35BOOST_FIXTURE_TEST_SUITE(const_string_test, F)
36
37
38
39BOOST_FIXTURE_TEST_CASE( test_case1, F )
40
41{
42
43 BOOST_CHECK( i == 1 );
44
45 ++i;
46
47}
48
49
50
51BOOST_FIXTURE_TEST_CASE( test_case2 )
52
53{
54
55 BOOST_CHECK_EQUAL( i, 1 );
56
57}
58
59
60
61BOOST_AUTO_TEST_CASE( test_case3 )
62
63{
64
65 BOOST_CHECK( true );
66
67}
68
69
70
71BOOST_AUTO_TEST_SUITE_END()
72
73
使用宏BOOST_FIXTURE_TEST_SUITE在第二個參數中指定夾具來代替宏BOOST_AUTO_TEST_SUITE以建立測試套件,夾具將在該測試套件的所有測試用例中可用。使用宏BOOST_FIXTURE_TEST_CASE代替宏BOOST_AUTO_TEST_CASE建立測試用例,可以在該測試用例中使用夾具。需要注意的是在每一個測試用例中都將會執行夾具的安裝和卸載過程。
5.測試輸出
Boost Test中包括了十個級別的日志信息:
成功信息
測試樹往返移動通知
通用信息
警告信息
非致命錯誤信息
未捕獲C++異常
致命系統錯誤
所有信息
無信息
以上級別以由低到高的級別排列,可以在運行時參數log_level中設置,還可以使用參數log_format來制定輸出格式。
可以向測試程序傳遞參數來定制測試結果,如“test.exe –build_info=yes –log_level=all”下面是常用的測試參數:
build_infoa:設置為yes將在開始運行測試前打印當前的操作系統版本、編譯器版本等信息;
log_level:對應於十個日志級別,包括all、success、test_suite、message、warning、error、cpp_exception、system_error、fatal_error、nothing。
output_format:定義日志的輸出格式。目前支持兩種格式,HRF(可讀格式)和XML格式;
run_test:指定要運行的測試單元,包括測試套件與測試用例,可以使用通配符“*”來制定運行符合特定條件的測試單元,如“test.exe –run_test=*/test1”將運行所有名為test1並位於主套件直接子套件的測試單元;
show_progress:設置為yes將在運行測試時顯示當前的進度。
最後,向Boost文檔漢化小組表示由衷的感謝。謝謝你們~