2008-11-06 60 views
15

我使用Boost測試框架來單元測試我的C++代碼,並想知道是否可以測試一個函數是否可以斷言?是的,聽起來有點奇怪,但忍耐着我!我的許多函數在輸入時檢查輸入參數,斷言它們是否無效,並且對此進行測試會很有用。例如:在Boost測試框架中斷言的測試

void MyFunction(int param) 
{ 
    assert(param > 0); // param cannot be less than 1 
    ... 
} 

我希望能夠做這樣的事情:

BOOST_CHECK_ASSERT(MyFunction(0), true); 
BOOST_CHECK_ASSERT(MyFunction(-1), true); 
BOOST_CHECK_ASSERT(MyFunction(1), false); 
... 

您可以檢查異常使用Boost測試,所以我想知道是否有一些斷言魔術也被拋出。 ..

+0

我知道這是一個老話題,但我說我想出了在工作順手拿一個解決方案。 – grokus 2011-07-26 22:26:19

回答

5

我不這麼認爲。你總是可以編寫你自己的assert,它引發一個異常,然後使用BOOST_CHECK_NOTHROW()來處理這個異常。

0

對不起,但你是以錯誤的方式攻擊你的問題。

「斷言」是魔鬼(a.k.a.「C」)的產卵,對任何有適當例外的語言都是無用的。 waaaaaay更好的是重新實現一個類似assert的功能和異常。這樣,您實際上有機會以正確的方式處理錯誤(包括正確的清理過程)或隨意觸發它們(用於單元測試)。另外,如果你的代碼在Windows中運行,當你斷言失敗時,你會得到一個無用的彈出窗口,提供你調試/放棄/重試。很好的自動化單元測試。

所以,你自己一個忙,重新編寫一個拋出異常的斷言函數。這裏有一個: How can I assert() without using abort()?

將它包裝在一個宏中,以便獲得_ _FILE _和_ _ LINE _ _(對調試有用),然後就完成了。

+12

如果你認爲這是你使用錯誤的方式。斷言只能用於必須爲真或者是*程序員*錯誤的東西,請參閱問題中給出的示例。如果程序員以錯誤的方式使用某個函數,您希望儘快知道該函數。 – 2008-11-07 12:44:17

+1

我同意安德烈亞斯。斷言和可能的情況下static_asserts不應該被例外替代。它們旨在檢測程序員的錯誤。 – 2008-11-07 20:08:24

9

有兩種我喜歡檢查的錯誤:不變量和運行時錯誤。

無論怎樣,不變量都是應該始終如一的事情。對於那些,我使用斷言。像你這樣的事情不應該給我一個零指針給你給我的輸出緩衝區。這是代碼中的一個錯誤,簡單明瞭。在調試版本中,它會斷言並給我一個修正它的機會。在零售版本中,它會導致訪問衝突,並生成一個小型轉儲(Windows,至少在我的代碼中)或coredump(Mac/unix)。沒有catch,我可以做,這有意義處理解引用零指針。在Windows catch (...)可以抑制訪問違規行爲,並給用戶一種錯誤的自信感,即當事情已經發生可怕,可怕的錯誤時,事情就會好轉。

這就是爲什麼我開始相信,catch (...)一般是在C代碼味道++和在那裏我可以認爲存在的唯一合理的地方是在main(或WinMain)你生成一個核心權利之前轉儲並禮貌地退出該應用程序。

運行時錯誤類似於「由於權限而無法寫入此文件」或「由於磁盤已滿而無法寫入此文件」。對於這些類型的錯誤拋出異常是有道理的,因爲用戶可以做一些事情,比如更改目錄權限,刪除一些文件或選擇其他位置來保存文件。這些運行時錯誤可由用戶更正。用戶不能糾正違反常量的情況,只能由程序員糾正。 (有時兩者是相同的,但通常不是。)

您的單元測試應強制代碼拋出代碼可能生成的運行時錯誤異常。您可能還想強制協作者的異常情況,以確保您的系統處於異常安全狀態。

但是,我不相信在試圖強制你的代碼對單元測試的不變量進行斷言時是有價值的。

3

我認爲這個問題和一些回覆會將運行時錯誤檢測與錯誤檢測相混淆。他們也混淆了意圖和機制。

運行時錯誤是100%正確的程序中可能發生的事情。它需要檢測,並且需要適當的報告和處理,並且應該進行測試。錯誤也會發生,爲了程序員的方便,最好先使用先決條件檢查或不變檢查或隨機斷言來捕捉它們。但這是程序員的工具。這個錯誤信息對普通用戶來說是沒有意義的,而且對正確編寫程序永遠不會傳遞給它的數據測試函數行爲似乎並不合理。

至於意圖和機制,應該指出的是,異常沒有什麼魔力。前段時間,Peter Dimov在Boost郵件列表(大約)上說,「例外只是非本地跳轉機制」。這是非常真實的。如果您的應用程序可能會在發生內部錯誤後繼續執行,而沒有在修復之前損壞某些內容的風險,則可以實施導致C++異常的自定義斷言。但它不會改變意圖,也不會使斷言測試更合理。

12

遇到同樣的問題,我通過文檔(和代碼)和 找到了一個「解決方案」。

Boost UTF使用boost::execution_monitor(在 <boost/test/execution_monitor.hpp>)。這樣做的目的是爲了抓住 在測試執行過程中可能發生的一切。當發現斷言 execution_monitor攔截它並拋出boost::execution_exception。因此, 通過使用BOOST_REQUIRE_THROW您可能會斷言斷言失敗。

這樣:

#include <boost/test/unit_test.hpp> 
#include <boost/test/execution_monitor.hpp> // for execution_exception 

BOOST_AUTO_TEST_CASE(case_1) 
{ 
    BOOST_REQUIRE_THROW(function_w_failing_assert(), 
         boost::execution_exception); 
} 

應該做的伎倆。 (這對我的作品。)

但是(或聲明):

  • 這對我的作品。也就是說,在Windows XP上,MSVC 7.1,boost 1.41.0。它可能 不適合您的設置或損壞。

  • 它可能不是Boost Test作者的意圖。 (雖然它似乎是execution_monitor的目的)。

  • 它會以同樣的方式處理每種形式的致命錯誤。我可能是 ,你的斷言以外的東西是失敗的。在這種情況下,您可能會錯過內存損壞錯誤,和/或錯過失敗的失敗斷言。

  • 它可能會打破未來的升壓版本。

  • 我期望如果在發佈配置中運行,它會失敗,因爲斷言將被禁用,並且斷言被設置爲阻止的代碼將運行 。導致非常不明確的行爲。

  • 如果在發佈配置爲msvc,一些斷言或其他致命錯誤 反正會發生它不會被捕獲。 (請參閱execution_monitor文檔)。

  • 如果你使用assert或不是由你決定。我喜歡他們。

參見:

此外,由於Gennadiy Rozental(升壓試驗的作者),如果你碰巧 閱讀,偉大的工作!

2

在工作中,我遇到了同樣的問題。我的解決方案是使用編譯標誌。當我的GROKUS_TESTABLE標誌位於我的GROKUS_ASSERT時會變成一個異常,通過Boost你可以測試引發異常的代碼路徑。當GROKUS_TESTABLE關閉時,GROKUS_ASSERT被轉換爲C++ assert()。

#if GROKUS_TESTABLE 
#define GROKUS_ASSERT ... // exception 
#define GROKUS_CHECK_THROW BOOST_CHECK_THROW 
#else 
#define GROKUS_ASSERT ... // assert 
#define GROKUS_CHECK_THROW(statement, exception) {} // no-op 
#endif 

我最初的動機是爲了幫助調試,即斷言()可以快速調試和異常往往是很難在gdb調試。我的編譯標誌似乎很好地平衡了可調試性和可測試性。

希望這有助於