本文是gtest高級測試指南的譯文,由於文章太長,分上下兩部分。
一、簡介
本文檔將向您展示更多的斷言,以及如何構造復雜的失敗消息,傳播致命的故障,重用和加速您的測試夾具,並在您的測試使用各種標志。
二、更多斷言
本節包括一些不太常用,但仍然重要的斷言。
2.1 顯式成功和失敗
這三個斷言實際上不測試值或表達式。 相反,它們直接產生成功或失敗。 與實際執行測試的宏類似,您可以將自定義失敗消息流入它們。
SUCCEED();
生成成功。 這不會使整體測試成功。 只有當測試在其執行期間沒有任何斷言失敗時,測試才被認為是成功的。
注意:SUCCEED()是純紀錄片,目前不生成任何用戶可見的輸出。 但是,我們可能會在未來向Google Test的輸出中添加SUCCEED()消息。
FAIL(); ADD_FAILURE(); ADD_FAILURE_AT("file_path",line_number);
FAIL()產生致命故障,而ADD_FAILURE()和ADD_FAILURE_AT()產生非致命故障。 當控制流而不是布爾表達式確定測試的成功或失敗時,這些是有用的。 例如,您可能想要寫如下:
switch(expression) { case 1: ... some checks ... case 2: ... some other checks ... default: FAIL() << "We shouldn't get here."; }
注意:你只能在返回void的函數中使用FAIL()。 有關詳細信息,請參閱 Assertion Placement section 部分。
2.2 異常斷言
這些用於驗證一段代碼拋出(或不拋出)給定類型的異常:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_THROW( statement, exception_type); |
EXPECT_THROW( statement, exception_type); |
statement throws an exception of the given type |
ASSERT_ANY_THROW( statement); |
EXPECT_ANY_THROW( statement); |
statement throws an exception of any type |
ASSERT_NO_THROW( statement); |
EXPECT_NO_THROW( statement); |
statement doesn't throw any exception |
Examples:
ASSERT_THROW(Foo(5), bar_exception); EXPECT_NO_THROW({ int n = 5; Bar(&n); });
三、更好的錯誤消息的謂詞斷言
雖然Google測試有一套豐富的斷言,但它們永遠不可能完整,因為它不可能(也不是一個好主意)預測用戶可能遇到的所有情況。 因此,有時用戶必須使用EXPECT_TRUE()來檢查復雜表達式,因為缺少更好的宏。 這有一個問題,沒有顯示你的表達式的部分的值,使得很難理解什么錯誤。 作為解決方法,一些用戶選擇自己構造失敗消息,將其流式傳輸到EXPECT_TRUE()。 然而,這是尷尬,特別是當表達式有副作用或評價昂貴。
Google測試提供三種不同的選項來解決這個問題:
3.1使用現有的布爾函數
如果你已經有一個函數或函數返回bool(或一個可以隱式轉換為bool的類型),你可以在謂詞斷言中使用它來獲得免費打印的函數參數:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED1( pred1, val1); |
EXPECT_PRED1( pred1, val1); |
pred1(val1) returns true |
ASSERT_PRED2( pred2, val1, val2); |
EXPECT_PRED2( pred2, val1, val2); |
pred2(val1, val2) returns true |
... | ... | ... |
在上面,predn是一個n元謂詞函數或函子,其中val1,val2,...和valn是它的參數。 如果謂詞在應用於給定參數時返回true,則斷言成功,否則失敗。 當斷言失敗時,它打印每個參數的值。 在任何一種情況下,參數只計算一次。
Here's an example:
// Returns true iff m and n have no common divisors except 1. bool MutuallyPrime(int m, int n) { ... } const int a = 3; const int b = 4; const int c = 10;
斷言EXPECT_PRED2(Mutual Prime,a,b); 將成功,而斷言EXPECT_PRED2(MutuallyPrime,b,c); 將失敗。
!MutuallyPrime(b, c) is false, where b is 4 c is 10
注意:
1. 如果在使用ASSERT_PRED *或EXPECT_PRED *時看到編譯器錯誤“no matching function to call(無匹配函數調用)”,請參閱此常見問題解答 this FAQ 以了解如何解決它。
2. 目前我們只提供arity <= 5的謂詞斷言。如果你需要更高級的斷言,讓我們知道。
3.2 使用返回AssertionResult的函數
雖然EXPECT_PRED *()和friends對快速工作很方便,但是語法不令人滿意:你必須使用不同的宏不同的arities,它感覺更像Lisp而不是C ++。 :: testing :: AssertionResult類解決了這個問題。
AssertionResult對象表示斷言的結果(無論它是成功還是失敗,以及相關聯的消息)。 您可以使用以下工廠函數之一創建AssertionResult:
namespace testing { // Returns an AssertionResult object to indicate that an assertion has // succeeded. AssertionResult AssertionSuccess(); // Returns an AssertionResult object to indicate that an assertion has // failed. AssertionResult AssertionFailure(); }
然后,您可以使用<<運算符將消息流式傳輸到AssertionResult對象。
要在布爾斷言(例如EXPECT_TRUE())中提供更多可讀消息,請編寫一個返回AssertionResult而不是bool的謂詞函數。 例如,如果您將IsEven()定義為:
::testing::AssertionResult IsEven(int n) { if ((n % 2) == 0) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << n << " is odd"; }
而不是:
bool IsEven(int n) { return (n % 2) == 0; }
the failed assertion EXPECT_TRUE(IsEven(Fib(4)))
will print:
Value of: IsEven(Fib(4)) Actual: false (*3 is odd*) Expected: true
instead of a more opaque:
Value of: IsEven(Fib(4)) Actual: false Expected: true
如果您希望在EXPECT FALSE和ASSERT_FALSE中看到提供信息的消息,並且在成功的情況下使謂詞變慢,您可以提供一個成功消息:
::testing::AssertionResult IsEven(int n) { if ((n % 2) == 0) return ::testing::AssertionSuccess() << n << " is even"; else return ::testing::AssertionFailure() << n << " is odd"; }
Then the statement EXPECT_FALSE(IsEven(Fib(6)))
will print
Value of: IsEven(Fib(6)) Actual: true (8 is even) Expected: false
3.3 使用謂詞格式化
如果你發現由(ASSERT | EXPECT)_PRED *和(ASSERT | EXPECT)_(TRUE | FALSE)生成的默認消息不令人滿意,或者您的謂詞的某些參數不支持流到ostream,您可以使用以下謂詞 - 格式化程序斷言 以完全自定義消息的格式化:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED_FORMAT1( pred_format1, val1); |
EXPECT_PRED_FORMAT1( pred_format1, val1); |
pred_format1(val1) is successful |
ASSERT_PRED_FORMAT2( pred_format2, val1, val2); |
EXPECT_PRED_FORMAT2( pred_format2, val1, val2); |
pred_format2(val1, val2) is successful |
... |
... |
... |
這和前兩組宏的區別是,不是一個謂詞,(ASSERT | EXPECT)_PRED_FORMAT *采用謂詞格式化器(pred_formatn),它是一個函數或函數簽名:
::testing::AssertionResult PredicateFormattern(const char*expr1, const char*expr2, ... const char*exprn, T1val1, T2val2, ... Tnvaln);
四、浮點比較
比較浮點數是棘手的。 由於舍入誤差,兩個浮點不太可能完全匹配。 因此,ASSERT_EQ的幼稚比較通常不起作用。 並且由於浮點可以具有寬的值范圍,沒有單個固定誤差界限工作。 最好通過固定的相對誤差界限進行比較,除了接近0的值由於精度的損失。
一般來說,對於浮點比較有意義,用戶需要仔細選擇誤差界限。 如果他們不想要或關心,根據最后地點(ULP)中的單位進行比較是一個很好的默認值,Google測試提供了斷言來做到這一點。 關於ULP的完整詳細信息相當長; 如果你想了解更多,請參閱這篇關於浮動比較的文章 this article on float comparison.。
Floating-Point Macros
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_FLOAT_EQ( val1, val2); |
EXPECT_FLOAT_EQ( val1, val2); |
the two float values are almost equal |
ASSERT_DOUBLE_EQ( val1, val2); |
EXPECT_DOUBLE_EQ( val1, val2); |
the two double values are almost equal |
“幾乎相等”是指兩個值彼此在4個ULP內。
以下斷言允許您選擇可接受的誤差界限:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_NEAR( val1, val2, abs_error); |
EXPECT_NEAR (val1, val2, abs_error); |
the difference between val1 and val2 doesn't exceed the given absolute error |
。。。。太多,需要時再去看。
五、Windows HRESULT斷言
這些斷言測試HRESULT成功或失敗。
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_HRESULT_SUCCEEDED( expression); |
EXPECT_HRESULT_SUCCEEDED( expression); |
expression is a success HRESULT |
ASSERT_HRESULT_FAILED( expression); |
EXPECT_HRESULT_FAILED( expression); |
expression is a failure HRESULT |
生成的輸出包含與expression返回的HRESULT代碼相關聯的人工可讀錯誤消息。
You might use them like this:
CComPtr shell; ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application")); CComVariant empty; ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
六、類型斷言
::testing::StaticAssertTypeEq<T1, T2>();
您可以調用該函數,來聲稱斷言類型T1和T2是相同的。 如果滿足斷言,該函數不執行任何操作。 如果類型不同,函數調用將無法編譯,編譯器錯誤消息(取決於編譯器)將顯示T1和T2的實際值。 這主要在模板代碼中有用。
注意:當在類模板或函數模板的成員函數中使用時,StaticAssertTypeEq <T1,T2>()僅在函數實例化時有效。 例如,給定:
template <typename T> class Foo { public: void Bar() { ::testing::StaticAssertTypeEq<int, T>(); } };
the code:
void Test1() { Foo<bool> foo; }
將不會生成編譯器錯誤,因為Foo <bool> :: Bar()永遠不會實際實例化。 相反,您需要:
void Test2() { Foo<bool> foo; foo.Bar(); }
導致編譯器錯誤。
七、Assertion Placement(斷言放置)
你可以在任何C ++函數中使用斷言。 特別地,它不必是測試夾具類的方法。 一個約束是生成致命故障(FAIL *和ASSERT_ *)的斷言只能在void返回函數中使用。 這是Google測試不使用exceptions的后果。 如果將它放在一個非void函數中,你會得到一個令人困惑的編譯錯誤,如“error: void value not ignored as it ought to be”。
如果需要在返回非void的函數中使用斷言,一個選項是使函數返回out參數中的值。 例如,您可以將T2 Foo(T1 x)重寫為void Foo(T1 x,T2 * result)。 你需要確保* result包含一些合理的值,即使該函數過早返回。 由於函數現在返回void,你可以在它里面使用任何斷言。
如果更改函數的類型不是一個選項,則應該使用生成非致命失敗的斷言,例如ADD_FAILURE *和EXPECT_ *。
注意:根據C ++語言規范,構造函數和析構函數不被視為void返回函數,因此您不能在其中使用致命斷言。 如果你嘗試,你會得到一個編譯錯誤。 一個簡單的解決方法是將構造函數或析構函數的整個體轉移到私有void返回方法。 然而,你應該意識到,構造函數中的致命斷言失敗並不會終止當前的測試,正如你的直覺所暗示的那樣; 它只是從構造函數早期返回,可能使您的對象處於部分構造狀態。 同樣,析構函數中的致命斷言失敗可能使您的對象處於部分破壞狀態。 在這些情況下仔細使用斷言!
八、教學Google測試如何打印您的值
當測試聲明(如EXPECT_EQ)失敗時,Google Test會打印參數值以幫助您調試。 它使用用戶可擴展值打印機。
此打印機知道如何打印內置的C ++類型,native數組,STL容器和任何支持<<運算符的類型。 對於其他類型,它打印值中的原始字節,並希望用戶可以計算出來。
如前所述,打印機是可擴展的。 這意味着你可以教它做一個更好的工作,打印你的特定類型,而不是轉儲字節。 要做到這一點,定義<<您的類型:
#include <iostream> namespace foo { class Bar { ... }; // We want Google Test to be able to print instances of this. // It's important that the << operator is defined in the SAME // namespace that defines Bar. C++'s look-up rules rely on that. ::std::ostream& operator<<(::std::ostream& os, const Bar& bar) { return os << bar.DebugString(); // whatever needed to print bar to os } } // namespace foo
有時,這可能不是一個選項:你的團隊可能認為它的壞風格有一個<<運算符的Bar,或者Bar可能已經有一個<<運算符,不做你想要的(你不能改變它)。 如果是這樣,您可以定義一個PrintTo()函數,如下所示:
#include <iostream> namespace foo { class Bar { ... }; // It's important that PrintTo() is defined in the SAME // namespace that defines Bar. C++'s look-up rules rely on that. void PrintTo(const Bar& bar, ::std::ostream* os) { *os << bar.DebugString(); // whatever needed to print bar to os } } // namespace foo
如果您定義了<<和PrintTo(),后者將在Google測試時使用。 這允許您自定義值如何顯示在Google測試的輸出中,而不影響依賴於其<<運算符的行為的代碼。
如果你想使用Google Test的值打印機自己打印一個值x,只需調用:: testing :: PrintToString(x),它返回一個std :: string:
vector<pair<Bar, int> > bar_ints = GetBarIntVector(); EXPECT_TRUE(IsCorrectBarIntVector(bar_ints)) << "bar_ints = " << ::testing::PrintToString(bar_ints);
Extending Google Test by Handling Test Events
這個挺復雜,寫在單獨的文檔中: http://www.cnblogs.com/jycboy/p/gtest_handlingEvent.html
轉載請注明出處:http://www.cnblogs.com/jycboy/p/gtest_AdvancedGuide.html