boost之测试
C/C++只提供了很有限的正確性驗證/測試支持——assert宏(沒錯,它是一個宏,雖然它違背常識使用了小寫的形式),這是很不夠的。C++標準中的 std: : exception能夠處理運行時異常、但并不能檢查代碼的邏輯,缺乏足夠的、語言級別的工具來保證軟件的正確性,使程序員很容易陷入與bug搏斗的泥沼中。
Boost 在這方面前進了一大步: boost.assert庫增強了原始的運行時assert 宏,static_assert庫提供了靜態斷言(編譯期診斷),而 lightweight_test(輕量)和 test庫則構建了完整的單元測試框架。
assert
為使用boost.assert需要包含頭文件<boost/assert.hpp>,
即:#include <boost/assert.hpp>
基本用法
# define BOOST_ASSERT(expr)? ? ? ? ? ? ? ? ? ? ?assert (expr)
#define BOOST_ASSERT_MSG(expr, msg)? ? ?assert(expr)&&(msg))?
宏的參數expr表達式可以是任意(合法的)c++表達式,從簡單的關系比較到復雜的函數嵌套調用都允許。如果表達式值為true,那么斷言成立,程序會繼續向下執行,否則斷言會引發一個異常,在終端上輸出調試信息并終止程序的執行。
BOOST_ASSERT宏僅會在 debug模式下生效,在release模式下不會進行編譯,不會影響到運行效率,所以可以放心大膽地在代碼中使用BoosT_ASSERT 斷言。它不僅能夠診斷錯誤、保證程序正確運行,而且其規范的大寫形式也能夠起到類似代碼注釋的作用,提醒代碼維護人員什么該做、什么不該做。
double func(int x) {BOOST_ASSERT_MSG(x!=0,"divided by zero");return 1.0/x; }禁用斷言
如果在頭文件<boost/assert.hpp>之前定義了宏 BOOST_DISABLE_ASSERTS,那么BOOST_ASSERT將會定義為((void)0),自動失效。但標準的assert宏并不會受影響,這可以讓程序員有選擇地關閉BOOST_ASSERT。
#include<cassert> #include<iostream> #include<boost/assert.hpp> #define BOOST_DISABLE_ASSERTSdouble func(int x) {BOOST_ASSERT(x!=0&&"divided by zero");std::cout<<"after boost_assert"<<std::endl;assert(x!=0&&"divided by zero");std::cout<<"after assert"<<std::endl;return 1.0/x; }int main() {func(0); } //Assertion `x!=0&&"divided by zero"' failed.擴展用法
如果在頭文件<boost/assert.hpp>之前定義了宏BOOST_ENABLE_ASSERT_HANDLER,這將導致BOOST_ASSERT的行為發生改變:
它將不再等同于assert宏,斷言的表達式無論是在 debug還是release模式下都將被求值。如果斷言失敗,會發生一個斷言失敗的函數調用boost::assertion_failed ()或者assertion_failed_msg()——這相當于提供了一個錯誤處理handlere
?static_assert
static_assert庫把斷言的診斷時刻由運行期提前到編譯期,讓編譯器檢查可能發生的錯誤,能夠更好地增加程序的健壯性,它模擬實現了C++標準里的static assert關鍵字。
用法
BOOST_STATIC_ASSERT使用了較復雜的技術,但簡單來理解,它實際上最終是一個typedef,因此在編譯時同樣不會產生任何代碼和數據,對運行效率不會有任何影響——不論是 debug模式還是release模式。
BOOST_STATIC_ASSERT是一個編譯期斷言,使用了模板元編程技術實現,雖然在很多方面它都與 BO0ST_ASSERT很相似,但用法還是有所不同的。最重要的區別是使用范圍:BOOST_ASSERT (expr)必須是一個能夠執行的語句,它只能在函數域里出現,而 BOOSTSTATIC_ASSERT則可以出現在程序的任何位置:名字空間域、類域或函數域。
template<typename T> T my_init(T a,T b) { BOOST_STATIC_ASSERT(sizeof(T)<sizeof(int)) return a<b? a:b; } int main() {std::cout<<my_init((short)1,(short)3); std::cout<<my_init(1L,3L);} // 如果使用支持C++11的編譯器(如ccc4.6以上、clang3.o 以上),那么我們可以使用BOOST_STATIC_ASSERT_MSG來輸出更明確的錯誤信息提示: BOOST_STATIc_ASSERT_MSG(sizeof(T)< sizeof(int), "only short or char");這樣提示信息會是: error: static assertion failed: only short or char使用建議:
static assert主要在泛型編程或模板元編程中用于驗證編譯期常數或者模板類型參數,可能讀者暫時不會用到它,但隨著對C++認識的深入,將來就會發現它的好處。
使用static_assert時還需要注意,斷言的表達式必須能夠在編譯期求值,可能會讓很多人不太適應,這很正常,因為這正是邁向C++泛型編程和模板元編程的第一步。
test
test庫提供了一個用于單元測試的基于命令行界面的測試套件“Unit TestFramework”,簡稱UTF,還附帶有檢測內存泄漏的功能,比其他的單元測試庫更強大更方便好用。它不僅能夠支持簡單的測試,也能夠支持全面的單元測試,并且還具有程序運行監控功能,是一個用于保證程序正確性的強大工具。
test庫包含兩個庫文件,需要編譯才能使用,在jamfile里指定lib的語句如下:
lib boost_unit_test_framework ;//單元測試框架庫
lib boost test exec monitor;\\//程序執行監視庫
#include<boost/test/unit_test.hpp>
最小化測試
#include<boost/test/minimal.hpp>
入口函數:
minimal test內部已經實現了main ( ),因此我們不必再編寫自己的main(),只需再要實現一個test_main ()函數,它是minimal test的真正功能函數。
test_main ()函數的聲明與標準的main ()很相似:
int test main ( int argc, char* argv [])
測試斷言
提供四個測試斷言宏
| BOOST_CHECK(e) | 斷言測試通過 不通過不影響程序執行 |
| BOOST_REQUIRE(e) | 要求測試必須通過 否則程序停止執行 |
| BOOST_ERROR(S) | 給出一個錯誤信息 程序繼續執行 |
| BOOST_FAIL(s) | 給出一個錯誤 程序運行終止進行 |
實例測試format
#include<boost/format.hpp> #include<boost/test/minimal.hpp> #include<iostream> int test_main(int argc,char* argv[]) {using namespace boost;format fmt("%d-%d");BOOST_CHECK(fmt.size()!=0);fmt %12 %34;BOOST_REQUIRE(fmt.str()=="12-34");BOOST_ERROR("演示一條錯誤信息");fmt.clear(); fmt%12; try {std::cout<<fmt; } catch(...) {BOOST_FAIL("致命錯誤,測試終止"); } return 0;} // 演示一條錯誤信息 in function: 'int test_main(int, char**)' 致命錯誤,測試終止 in function: 'int test_main(int, char**)'單元測試框架
test庫提供了強大的單元測試框架(UTF),它為軟件開發的基本領域——單元測試提供了簡單而富有彈性的解決方案,可以滿足開發人員從高到低的各種需求,它的優點包括:
易于理解,任何人都可以很容易地構建單元測試模塊;
提供測試用例、測試套件的概念,并能夠以任意的復雜度組織它們;提供豐富的測試斷言,能夠處理各種情況,包括C++異常;
可以很容易地初始化測試用例、測試套件或者整個測試程序;可以顯示測試進度,對于大型測試非常有用;
測試信息可以顯示為多種格式,如普通文本或者XML格式;支持命令行,可以指定運行任意一個測試套件或測試用例;
支持命令行,可以指定運行任意一個測試套件或測試用例;
還有許多更高級的用法。
測試斷言
test 庫同樣提供minimal test 里的四個基本的測試斷言: BOOST_CHECK、BOOSTREQUIRE、BOOST_ERROR和 BOOST_FAIL(但實現是不同的)。它們是最基本的測試斷言,能夠在任何地方使用,但同時為了通用性也不具有其他斷言的好處,我們應當盡量少使用。
測試斷言的形式是 BOOST_LVL_XXX,例如BOOST_CHECK_EQUAL、BOOST_WARN_GT,詳細命名規則如下:
BOOST??BOOST
遵循Boost庫的命名規則,宏一律以大寫的 BOOST開頭;
LVL
斷言的級別。WARN是警告級,不影響程序運行,也不增加錯誤數量;CHECK是檢查級別,如果斷言失敗增加錯誤數量,但不影響程序運行:REQUIRE是最高的級別,如果斷言失敗將增加錯誤數量并終止程序運行。最常用的斷言級別是CHECK,WARN可以用于不涉及程序關鍵功能的測試,只有當斷言失敗會導致無法繼續進行測試時才能夠使用REQUIRE;
XXX
各種具體的測試斷言,如斷言相等/不等、拋出/不拋出異常、大于或小于等。
常用的斷言:
| BOOST_LVL_EQUAL(1,r) | 檢測l==r,當測試失敗時會給出詳細的信息。它不能用于浮點數的比較,浮點數的相等比較應使用 BOOST_LVL_CLOSE; |
| BOOST_LVL_GE(1,r) | :檢測l>=r,同樣的還有GT (l>r)、LT ( l<r)、LE(l<=r)和 NE( l!=r),它們用于測試各種不等性; |
| BOOST_LVL_THROW(e,ex) | 檢測表達式e拋出指定的ex異常; |
| BOOST_LVL_NO_THROW(e) | 檢測表達式e不拋出任何異常; |
| BOOST_LVL_MESSAGE(e,msg) | 它與不帶 MESSAGE后綴的斷言功能相同,但測試失敗時給出指定的消息; |
| BOOST_TEST_NESSAGE(msg) | 它僅輸出通知用的信息,不含有任何警告或者錯誤,默認情況不會顯示。 |
測試主體
test庫將測試程序定義為一個測試模塊,由測試安裝、測試主體、測試清理和測試運行器四個部分組成。測試主體是測試模塊的實際運行部分,由測試用例和測試套件組織成測試樹的形式。
測試用例:
測試用例是一個包含多個測試斷言的函數,它是可以被獨立執行測試的最小單元,各個測試用例之間是無關的,發生的錯誤不會影響到其他測試用例。
要添加測試用例需要向UTF注冊。在 test庫中,可以采用手工或者自動兩種形式,通常自動的方式更加簡單易用,可以簡化測試代碼的編寫。我們使用宏 BOOST_AoTO_TEST_CASE像聲明函數一樣創建測試用例,它的定義是:
#define BOOST_AUTO_TEST_CASE(test_name) BOOST_AUTO_TEST_CASE(t_casel) {BOOST_CHECK_EQUAL(1,1)'.... }測試套件
測試套件是測試用例的容器,它包含一個或多個測試用例,可以將繁多的測試用例分組管理,共享安裝/清理代碼,更好地組織測試用例。
測試套件同樣可以有手工和自動兩種使用方式,
自動方式使用兩個宏 BOOST_AUTO_TEST_SUITE和BOOST_AUTO_TEST_SUITE_END,它們的定義是:
#define? BOOST_AUTO_TEST_SUITE(suite_name)
#define? BOOST_AUTO_TEST_SUITE_END;
這兩個宏必須成對使用,宏之間的所有測試用例都屬于這個測試套件。一個c++源文件中可以有任意多個測試套件,測試套件也可以任意嵌套,沒有深度的限制。測試套件的名字一律以s開頭,例如:
BOOST_AUto_TEST_SUITE((s_suite)BOOST_AUTO_TEST_CASE(t_case1) { BOOST_CHECK_EQUAL(1,1); .... } BOOST_AUTO_CASE(t_CASE2) { BOOST_CHECK_EQUAL(1,1); ... } BOOST_AUto_TEST_SUITE_END();主測試套件
任何UTF單元測試程序都必須存在唯一一個主測試套件,它是整個測試樹的根節點,其他的測試套件都是它的子節點。
主測試套件的定義可以使用宏BOOST_TEST_MAIN或者BOOST_TEST_MODULE,它們必須出現在<boost/test/unit_test.hpp>之前。
定義了主測試套件的源文件里可以不使用宏 BOOST_AUTO_TEST_SUITE 和 BOOST.AUTO_TEST_SUITE_END來組織測試用例,因為此時所有測試用例都自動屬于主測試套件(當然用了也不算錯)。
測試實例
#define BOOST_TEST_MAIN//定義主測試套件#include<boost/test/included/unit_test.hpp> #include<boost/smart_ptr.hpp>using namespace boost;BOOST_AUTO_TEST_SUITE(s_smart_prt);BOOST_AUTO_TEST_CASE(t_soped_ptr) {scoped_ptr<int>p(new int(857));BOOST_CHECK(p);BOOST_CHECK_EQUAL(*p,874);p.reset();BOOST_CHECK(p==0); } BOOST_AUTO_TEST_CASE(t_share_ptr) {shared_ptr<int>p1(new int(100));BOOST_CHECK(p1);BOOST_CHECK_EQUAL(*p1,100);BOOST_CHECK_EQUAL(p1.use_count(),1);shared_ptr<int>p2=p1;BOOST_CHECK(p2);BOOST_CHECK_EQUAL(p2.use_count(),2);*p1=255;BOOST_CHECK_EQUAL(*p1,225);BOOST_CHECK_GT(*p1,200); } BOOST_AUTO_TEST_SUITE_END() //Running 2 test cases... /root/boost/02/smart_ptr.cpp(17): error: in "s_smart_prt/t_soped_ptr": check *p == 874 has failed [857 != 874] /root/boost/02/smart_ptr.cpp(34): error: in "s_smart_prt/t_share_ptr": check *p1 == 225 has failed [255 != 225]*** 2 failures are detected in the test module "Master Test Suite"測試夾具
測試安裝執行測試前的準備工作,初始化測試用例或測試套件所需的數據。測試清理是測試安裝的反向操作,執行必要的清理工作。
測試安裝和測試清理的動作很像C++中的構造函數和析構函數,故可以定義一個輔助類,它的構造函數和析構函數分別執行測試安裝和測試清理,之后我們就可以在每個測試用例最開始聲明一個對象,它將自動完成測試安裝和測試清理。
基于這個基本原理,UTF中定義了“測試夾具”的概念,它實現了自動的測試安裝和測試清理,就像是一個夾在測試用例或測試套件兩端的“夾子”。測試夾具不僅可以用于測試用例,也可以用于測試套件和單元測試全局。
使用夾具 必須是一個類
struct test_fixture_name { test_fixture_name(){} ~test_fixture_name(){} }指定測試用例和測試套件的夾具類需要使用另外兩個宏:
#define BOOST_FIXTURE_TEST_SUITE( suite_name,fixture_name )
#define BOOST_FIXTURE_TEST_CASE ( test_name,fixture_name)
全局測試夾具需要使用另一個宏 BOOST_GLOBAL_FIXTURE,它定義的夾具類被應用于整個測試模塊的所有測試套件——包括主測試套件。
#define BOOST_TEST_MAIN #include<boost/test/included/unit_test.hpp>#include<boost/assign.hpp> #include<vector> #include<iostream> using namespace boost; // 全局 struct global_fixture{global_fixture(){std::cout<<("globakl setup\n");}~global_fixture(){std::cout<<("global teardown\n");} }; // 定義全局夾具類 BOOST_GLOBAL_FIXTURE(global_fixture);// 測試套件struct assign_fixture {assign_fixture(){std::cout<<("suit setip\n");}~assign_fixture(){std::cout<<("suit: teardown");}std::vector<int>v; }; BOOST_FIXTURE_TEST_SUITE(s_assign,assign_fixture);BOOST_AUTO_TEST_CASE(t_assign) {using namespace boost::assign;v+=1,2,3,4;BOOST_CHECK_EQUAL(v.size(),4);BOOST_CHECK_EQUAL(v[2],3);} BOOST_AUTO_TEST_CASE(t_assign2) {using namespace boost::assign;push_back(v)(10)(20)(30);BOOST_CHECK_EQUAL(v.size(),3);BOOST_CHECK_GE(v[0],v[1]); } //globakl setup Running 2 test cases... suit setip suit: teardownsuit setip /root/boost/02/assgin_test.cpp(43): error: in "s_assign/t_assign2": check v[0] >= v[1] has failed [10 < 20] suit: teardown *** 1 failure is detected in the test module "Master Test Suite" global teardownBOOST_AUTO_TEST_SUITE_END()
?
總結
- 上一篇: 用argis和envi出对比图,带roi
- 下一篇: SpringBoot Shiro 配置自