Google C++單元測試框架GoogleTest---AdvancedGuide(譯文)上


本文是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(statementexception_type); EXPECT_THROW(statementexception_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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM