簡介: 為什么谷歌 c + + 測試框架?
谷歌 c + + 測試框架可以幫助你寫出更好的 c + + 測試。
無論是否您工作在 Linux 上,Windows 或 Mac,如果您編寫 c + + 代碼,谷歌測試可以幫助你。
是什么讓一個好的測試,和谷歌 c + + 測試框架如何適應呢?我們相信:
- 測試應獨立和可重復。它是痛苦調試成功或失敗的其他測試結果的測試。谷歌 c + + 測試框架通過不同的對象上運行每個隔離測試。當測試失敗時,谷歌 c + + 測試框架允許您在隔離的快速調試中運行它。
- 測試應舉辦井和反映測試代碼的結構。谷歌 c + + 測試框架可以共享數據和子例程的測試用例入組相關的測試。這種常見模式很容易認出并使測試易于維護。這種一致性人切換項目,并開始一個新的代碼庫的工作時特別有用。
- 測試應攜帶和可重復使用。開放源代碼社區有很多平臺無關的代碼,其測試也應平臺無關。谷歌 c + + 測試框架在不同的操作系統,具有不同的編譯器 (gcc、 MSVC,和其他人),有無異常,那么谷歌 c + + 測試框架的測試可以輕松地使用各種配置。(注意當前版本只包含生成腳本用于 Linux — — 我們正在積極地對其他平臺的腳本)。
- 當測試失敗時,他們應盡可能提供問題盡可能多的信息。谷歌 c + + 測試框架不只是第一次測試失敗。相反,它只停止當前測試,并繼續下一步。您還可以設置測試該報告非致命故障后,當前測試繼續。因此,您可以檢測并修復單個編譯編輯-運行周期中的多個 bug。
- 測試框架應解放測試作家從內務管理家務,讓他們專注于測試的內容。谷歌 c + + 測試框架自動跟蹤的所有測試定義的并且不需要用戶枚舉它們為了運行它們。
- 測試應快速。與谷歌 c + + 測試框架,可以在測試之間重用共享的資源并支付集上/淚下只有一次,而不做測試相互依賴。
由于谷歌 c + + 測試框架基于流行 xUnit 建筑,你會感覺右在家里如果您使用過 JUnit 或前的 PyUnit。如果不是,它將帶你了解的基本知識和開始約 10 分鐘。所以我們去吧 !
注:我們有時稱為 Google c + + 測試框架非正式谷歌測試.
建立一個新的測試項目
要編寫一個使用谷歌測試的測試程序,您需要將谷歌測試編譯成庫和鏈接您的測試與它。對于一些受歡迎的生成系統生成文件,我們提供: msvc /Visual studio, xcode /為 Mac Xcode,使 /為 GNU,指令 / Borland c + + 生成器,和 autotools 腳本 (已過時) 和CMakeLists.txt的 CMake (推薦) 谷歌測試的根目錄中。如果您生成系統不在此列表中,您可以看一看學習 Google 測試應如何編譯使/生成文件(基本上您要編譯src/gtest-all.cc與GTEST_ROOT和GTEST_ROOT/包含在標題搜索路徑中,其中GTEST_ROOT是 Google 測試根目錄)。
一旦您將能夠編譯谷歌測試庫,應創建一個項目,或您的測試程序生成目標。請確保你有GTEST_ROOT/包含標題搜索路徑中,以便您的測試進行編譯時,編譯器可以找到"gtest/gtest.h" 。設置您的測試項目與谷歌測試庫鏈接 (例如,在 Visual Studio 中,這通過在gtest.vcproj上添加依賴項).
如果您還有問題,看看如何測試 Google 測試生成并將其用作示例。
基本概念
在使用谷歌測試時,你開始通過編寫斷言,哪些語句,檢查是否一個條件為真。斷言結果可以成功、非致命故障或致命故障。如果發生致命故障,它將中止當前的功能 ;否則,程序繼續正常。
測試使用斷言來驗證測試的代碼的行為。如果測試崩潰或已失敗的斷言,那么它將失敗;否則為它會成功.
測試用例包含一個或多個測試。您應將測試用例反映測試代碼的結構組成您的測試。當多個測試的測試用例需要共同的對象和子例程時,你能把它們測試夾具的類。
測試程序可以包含多個測試用例。
我們現在將說明如何編寫一個測試程序,開始在個別論斷一級和建立測試和測試用例。
斷言
谷歌測試斷言是類似于函數調用的宏。您通過使其行為的斷言測試類或函數。當斷言失敗時,谷歌測試打印斷言的源文件和行號的位置,連同失敗消息。您也可以提供自定義失敗消息,將追加到谷歌測試郵件。
斷言測試同樣的事情,但有不同的效果,對當前函數的成對出現。ASSERT_ *版本生成失敗時,致命故障和中止當前函數。EXPECT_ *版本生成非致命的失敗,不中止當前函數。EXPECT_ *通常首選,因為它們允許多個測試中報告故障。但是,您應該使用ASSERT_ *如果不合情理,則斷言失敗后繼續。
由于失敗ASSERT_ *從當前函數立即返回,可能會跳過清理代碼后,它可能會導致空間泄漏。取決于漏油的性質,它可能會或可能不值得修復-所以記住這一點如果你除了斷言錯誤堆檢查器錯誤。
若要提供自定義失敗消息,只是流它成宏使用<<運算符或這類商戶的序列。示例:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
任何可以傳輸到ostream的東西可以進行流式傳輸到斷言宏 — — 尤其是 C 字符串和字符串對象。如果寬字符串 (wchar_t *, TCHAR *在UNICODE模式下,在 Windows 中或std::wstring) 流入到一個斷言,它將被翻譯為 utf-8 打印時。
基本的斷言
這些斷言做真/假的基本條件測試。
致命的斷言 | 非致命的斷言 | 驗證 |
ASSERT_TRUE (條件); | EXPECT_TRUE (條件); | 條件為真 |
ASSERT_FALSE (條件); | EXPECT_FALSE (條件); | 條件為假 |
請記住,當他們失敗時, ASSERT_ *收益率出現致命故障和返回從當前函數,而EXPECT_ *產量為非致命的失敗,從而能夠繼續運行功能。在任一情況下,斷言失敗意味著它包含測試失敗。
可用性: Linux、 Windows、 mac。
二進制比較
本節介紹比較兩個值的斷言。
致命的斷言 | 非致命的斷言 | 驗證 |
ASSERT_EQ (, 預期實際); | EXPECT_EQ (, 預期實際); | 預期= =實際 |
ASSERT_NE (, val1 val2); | EXPECT_NE (, val1 val2); | val1! =val2 |
ASSERT_LT (, val1 val2); | EXPECT_LT (, val1 val2); | val1<val2 |
ASSERT_LE (, val1 val2); | EXPECT_LE (, val1 val2); | val1< =val2 |
ASSERT_GT (, val1 val2); | EXPECT_GT (, val1 val2); | val1>val2 |
ASSERT_GE (, val1 val2); | EXPECT_GE (, val1 val2); | val1> =val2 |
故障時,事件中谷歌測試打印在val1和val2 。ASSERT_EQ * EXPECT_EQ * (及其他所有我們稍后介紹的平等斷言),您應將您要測試的實際位置的表達式放并把其預期的價值放在預期,作為谷歌測試失敗的消息非常適合于本公約。
值參數必須是可比的說法比較運算符的否則你會編譯器錯誤。我們需要支持的參數使用<<以來 v1.6.0 運算符的流ostream,但它已不再需要 (如<<是支持,它將調用,則斷言失敗時打印參數 ; 否則將嘗試將其打印出來的最佳方法,它可以在谷歌測試。更多詳細信息以及如何自定義參數的打印,請參閱此 Google 模仿食譜.).
如果您定義相應的比較運算符,這些斷言可以使用用戶定義的類型,但僅限于 (例如= =, <,等等)。如果定義了相應的運算符,則更傾向于使用ASSERT_*()宏,因為他們會打印出結果的比較,但以及兩個操作數不只。
參數總是進行一次評估。因此,它是有副作用的參數確定。然而,與任何普通的 C/c + + 函數,一樣的參數的計算順序是未定義 (即編譯器是自由選擇任何順序) 和您的代碼不應依賴于任何特定的參數計算順序。
ASSERT_EQ()沒有指針的指針平等。如果使用兩個 C 字符串,它測試如果它們在相同的內存位置,如果它們具有相同的值。因此,如果您想通過值比較 C 字符串 (例如const char *),使用ASSERT_STREQ() ,將在稍后介紹的。特別是,聲稱是 C 字符串為空,使用ASSERT_STREQ NULL c_string) 。不過,若要比較兩個字符串對象,您應使用ASSERT_EQ.
這一節中的宏 (字符串和wstring這兩個狹窄和寬字符串對象的工作).
可用性: Linux、 Windows、 mac。
字符串比較
在此組中的斷言,比較兩個C 字符串。如果要比較兩個字符串對象,請使用EXPECT_EQ、 EXPECT_NE、 等代替。
致命的斷言 | 非致命的斷言 | 驗證 |
ASSERT_STREQ (, expected_str actual_str); | EXPECT_STREQ (, expected_str actual_str); | 兩個 C 字符串具有相同的內容 |
ASSERT_STRNE (, str1 str2); | EXPECT_STRNE (, str1 str2); | 兩個 C 字符串具有不同的內容 |
ASSERT_STRCASEEQ (, expected_stractual_str); | EXPECT_STRCASEEQ (, expected_stractual_str); | 兩個 C 字符串具有相同的內容,忽略案例 |
ASSERT_STRCASENE (, str1 str2); | EXPECT_STRCASENE (, str1 str2); | 兩個 C 字符串具有不同的內容,忽略案例 |
請注意斷言名稱中的"案例"意味著忽略大小寫。
* STREQ *和* STRNE *也接受寬的 C 字符串 (wchar_t *)。如果兩個寬字符串比較失敗,其值將打印為 UTF 8 窄字符串。
一個空字符串和NULL指針被視為不同.
可用性: Linux、 Windows、 mac。
另請參閱: 更多的字符串比較特技 (子串、 前綴、 后綴,和正則表達式匹配,例如),請參見高級谷歌測試指南.
簡單的測試
要創建一個測試:
- 使用TEST()宏來定義和測試函數命名,這些都是普通的 c + + 函數不會返回一個值。
- 在此函數中,與您要包括的任何有效的 c + + 語句一起使用各種谷歌測試斷言檢查值。
- 確定測試的結果,斷言 ;如果在測試中的任何斷言失敗 (致命或非致命),或如果測試崩潰,整個測試失敗。否則,它會成功。
TEST(test_case_name, test_name) {
... test body ...
}
TEST()參數從一般轉到特定。第一個參數是測試用例的名稱,第二個參數是內測試用例的測試的名稱。這兩個名稱必須是有效的 c + + 標識符,它們不應包含下劃線 (_)。測試的完整名稱由其包含測試用例和其個人名稱組成。來自不同測試用例的測試可以有個別的名稱相同。
例如,讓我們看一個簡單的整數函數:
int Factorial(int n); // Returns the factorial of n
此函數的測試用例可能類似于:
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(1, Factorial(0));
}
// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
谷歌測試組的測試結果的測試用例,所以邏輯上相關的測試應在同一測試用例 ;換句話說,他們TEST()的第一個參數應是相同的。在上面的示例中,我們有兩個測試, HandlesZeroInput和HandlesPositiveInput,屬于同一個測試用例FactorialTest.
可用性: Linux、 Windows、 mac。
測試夾具: 使用相同的數據配置多個測試
如果您發現自己相似的數據寫入兩個或更多的測試操作,您可以使用測試夾具。這樣,您就可以重復使用的對象的多個不同測試相同的配置。
若要創建夾具,只是:
- 派生類從:: testing::Test 。啟動其體保護:或公共: ,我們要從子類訪問夾具成員。
- 在類中聲明您計劃使用的任何對象。
- 如有必要,寫了默認的構造函數或SetUp()函數,準備每個測試的對象。一個常見的錯誤是,用一個小u - Setup()不要讓發生在你身上的拼寫SetUp() 。
- 如有必要,寫了析構函數或TearDown()函數釋放您在SetUp()中分配的任何資源。要了解何時應使用析構函數構造函數和何時應使用SetUp()/TearDown(),請閱讀此FAQ 條目.
- 如果需要,定義您要共享的測試的子例程。
使用時夾具,使用TEST_F()的TEST()而不是因為它允許您訪問對象和測試夾具的子例程:
TEST_F(test_case_name, test_name) {
... test body ...
}
像TEST(),第一個參數是測試用例的名稱,但對于TEST_F() ,這必須測試夾具類的名稱。您可能已經猜到: _F是夾具。
不幸的是,在 c + + 宏系統不允許我們能夠創建一個單一的宏,可以處理這兩種類型的測試。使用錯誤的宏將導致編譯器錯誤。
此外,在TEST_F()中,在使用之前必須首先定義測試夾具類,否則你會編譯器錯誤"' 虛擬類聲明外'"。
用TEST_F()定義的每個測試中,Google 測試將:
- 在運行時創建新的測試夾具
- 立即將其初始化通過SetUp() ,
- 運行測試
- 通過調用TearDown()清理
- 刪除測試夾具。請注意不同的測試,在同一個測試用例中有不同的測試夾具對象,創建下一個之前,谷歌測試總是刪除測試夾具。谷歌測試不會重用相同的測試夾具,為多個測試。一個測試使夾具的任何更改不會影響其他測試。
作為一個例子,讓我們寫測試 FIFO 隊列類命名隊列中,具有以下接口:
template <typename E> // E is the element type.
class Queue {
public:
Queue();
void Enqueue(const E& element);
E* Dequeue(); // Returns NULL if the queue is empty.
size_t size() const;
...
};
首先,定義夾具的類。按照約定,,你應該給它生成器美孚在哪里類名稱正在進行測試。
class QueueTest : public ::testing::Test {
protected:
virtual void SetUp() {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// virtual void TearDown() {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
在這種情況下,由于我們不需要每個測試,除已做的析構函數之后進行清理,則不需要TearDown() 。
現在我們會編寫測試使用TEST_F()和這種裝置。
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(0, q0_.size());
}
TEST_F(QueueTest, DequeueWorks) {
int* n = q0_.Dequeue();
EXPECT_EQ(NULL, n);
n = q1_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(1, *n);
EXPECT_EQ(0, q1_.size());
delete n;
n = q2_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(2, *n);
EXPECT_EQ(1, q2_.size());
delete n;
}
上述使用了ASSERT_ *和EXPECT_ *斷言。經驗法則是使用EXPECT_ *當您想要繼續斷言失敗后,透露更多的錯誤,并使用ASSERT_ *時繼續后失敗沒什么意義的測試。例如,出列測試中的第二個說法是ASSERT_TRUE (n! = NULL),就像我們要取消引用指針n后,這將導致 segfault n為NULL時.
當這些測試運行時,會發生下列情況:
- 谷歌測試構造讓 (我們稱之為t1 QueueTest對象 ).
- t1。SetUp()初始化t1 .
- T1上 ( IsEmptyInitially ) 的第一個測試運行 .
- t1。TearDown()測試完成后清理。
- t1被破壞。
- 這次運行的DequeueWorks測試上另一個QueueTest對象,重復上述步驟。
可用性: Linux、 Windows、 mac。
注: 谷歌測試自動保存所有谷歌測試標志時測試對象構造,并還原它們時它破壞。
調用測試
TEST()和TEST_F()隱式使用谷歌測試注冊他們的測試。所以,不同于與很多其他 c + + 測試框架,你不必為了運行它們 re-list 定義的所有測試。
定義您的測試之后, 您可以運行它們的RUN_ALL_TESTS() ,它返回0 ,如果所有的測試都成功或1否則。請注意RUN_ALL_TESTS()運行您的鏈接單元中的所有測試— — 它們可以從不同的測試案例或甚至不同的源文件。
當調用, RUN_ALL_TESTS()宏:
- 保存所有谷歌測試標志的狀態。
- 創建第一個測試夾具對象。
- 初始化它通過SetUp().
- 夾具對象上運行測試。
- 通過TearDown()的夾具清理.
- 刪除夾具。
- 恢復所有谷歌測試標志的狀態。
- 接下來的測試,重復上述步驟,直到所有的測試運行。
此外,如果文本夾具的構造函數生成致命故障在步驟 2 中,是沒有意義的步驟 3-5,因此被跳過。同樣,如果第 3 步生成一個致命的失敗,則將被跳過第 4 步。
重要提示: 您一定不能忽略的返回值RUN_ALL_TESTS(),或將給你gcc編譯器錯誤。這種設計的理由是自動化測試服務確定是否對其退出代碼,不是在它的標準輸出/stderr 輸出 ; 測試已通過基于因此你的main ()函數必須返回值的RUN_ALL_TESTS().
此外,您應調用RUN_ALL_TESTS()唯一一次。叫它不止一次沖突與一些高級功能 (如線程安全死亡測試) 的谷歌測試,因此不支持。
可用性: Linux、 Windows、 mac。
編寫函數 main)
您可以從這個樣板啟動:
#include "this/package/foo.h"
#include "gtest/gtest.h"
namespace {
// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if its body
// is empty.
FooTest() {
// You can do set-up work for each test here.
}
virtual ~FooTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for Foo.
};
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
const string input_filepath = "this/package/testdata/myinputfile.dat";
const string output_filepath = "this/package/testdata/myoutputfile.dat";
Foo f;
EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
}
// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
// Exercises the Xyz feature of Foo.
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
:: Testing::InitGoogleTest()函數解析命令行,因為谷歌測試標志,并刪除所有認可的標志。這允許用戶控制通過各種標志,我們將介紹在AdvancedGuide的測試程序的行為。您必須在調用RUN_ALL_TESTS()之前, 調用此函數或標志不會被正確初始化。
在 Windows 上, InitGoogleTest()同樣適用于寬字符串,因此它可以用于UNICODE模式以及在編譯的程序。
但也許你認為編寫所有那些 main () 函數是太多的工作嗎?我們完全同意你,這就是為什么 Google 測試提供 main () 的基本實現。如果它適合您的需要,然后只是鏈接您的測試與 gtest_main 庫,你是要走好。
對于 Visual c + + 用戶的重要說明
如果你把你的測試放在圖書館,你的main ()函數是在另一個庫或.exe 文件中將不會運行這些測試。原因是 Visual c + + 中的bug 。當您定義您的測試時,谷歌測試創建某些注冊他們的靜態對象。這些對象不從其他地方引用,但仍然應該運行它們的構造函數。Visual c + + 鏈接器看到什么庫中引用的其他地方時它會引發圖書館。你要從主程序從丟棄它保持鏈接器的測試引用您的庫。這里是如何做這件事。某處代碼庫中聲明的函數:
__declspec(dllexport) int PullInMyLibrary() { return 0; }
如果你把你的測試放在靜態庫 (而不是 DLL) 然后__declspec(dllexport)則不需要。現在,在 main 程序中寫一段代碼,它調用該函數:
int PullInMyLibrary();
static int dummy = PullInMyLibrary();
這會讓你的測試引用,會使他們自行注冊在啟動時。
此外,如果您在靜態庫中定義您的測試,添加/OPT:NOREF主程序鏈接器選項。如果您使用 MSVC + + IDE,轉到您的.exe 項目屬性/配置屬性/鏈接器/優化和設置的引用設置為保持未引用數據 (/ 選擇: NOREF)。這將使 Visual c + + 鏈接器丟棄單個符號生成最終的可執行文件從您的測試。
不過還有一個更多陷阱。如果您使用谷歌測試靜態庫 (即如何定義的 gtest.vcproj) 作為您的測試也必須位于一個靜態庫。如果你要讓他們在 DLL 中,您必須更改將建成一個 DLL 以及谷歌測試。否則為你的測試將不會正確運行,或者將不能運行。普遍的結論是: 使您的生活更輕松 — — 圖書館不寫你的測試 !
從這里去哪里
恭喜 !您已經了解谷歌測試基礎。可以開始編寫和測試運行谷歌測試、 閱讀一些樣本,或繼續AdvancedGuide,它描述了許多更有用的谷歌測試功能。
已知的局限性
谷歌測試被為了是線程安全的。執行是線程安全的pthreads圖書館在哪里可用的系統上。它目前是不安全使用谷歌測試斷言從兩個線程同時在其他系統 (如 Windows)。在大多數的測試中這不是問題,通常所斷言的事,主線程。如果你想幫助,您可以在您的平臺gtest port.h執行必要的同步基元志愿者。