1. 程式人生 > >Boost Test學習總結(C++)

Boost Test學習總結(C++)

1.常用的C++單元測試框架

測試驅動開發(TDD)已經是一種非常流行的開發方式了,在Java.Net中都提供了非常好的單元測試框架,最近研究C++下面的單元測試,發現其實在C++中還是有很多選擇:

  • CPPUnit:著名的XUnit系列產品之一,熟悉JUnitNUnit的開發人員可以很快上手。
  • CXXTest:需要進行預處理,需要安裝PerlPython
  • Boost Test:功能強大,提供了自動註冊和手動註冊兩種方式,更重要的是來自千錘百煉的Boost庫。
  • Google TestGoogle在去年開源的測試框架,據說其內部上千個專案均採用該框架進行單元測試。

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;
 
}
 

  示例程式碼將會分別以引數12345執行測試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}
; 自定義一個夾具非常簡單,只需要定義一個類,分別在類的建構函式和解構函式中進行安裝和拆卸。另外,類的公有成員在測試用例中可以直接引用而不需要加上物件名作為字首。例如:
#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:對應於十個日誌級別,包括allsuccesstest_suitemessagewarningerrorcpp_exceptionsystem_errorfatal_errornothing
  • output_format:定義日誌的輸出格式。目前支援兩種格式,HRF(可讀格式)和XML格式;
  • run_test:指定要執行的測試單元,包括測試套件與測試用例,可以使用萬用字元“*”來制定執行符合特定條件的測試單元,如“test.exe –run_test=*/test1”將執行所有名為test1並位於主套件直接子套件的測試單元;
  • show_progress:設定為yes將在執行測試時顯示當前的進度。