1. 安裝使用
1.1 安裝
在https://code.google.com/p/googletest/ 下載原始碼
進入msvc, 注意編譯方式, 如果是dll, 選擇 gtest-md
編譯生成lib檔案, 然後引入.檔案即可使用
1.2 使用
#include "gtest/gtest.h"
int _tmain(int argc, _TCHAR* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
當然我們也可以輸出到xml
int _tmain(int argc, _TCHAR* argv[])
{
testing::GTEST_FLAG(output) = "xml:";
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
2. 斷言
斷言的巨集可以分為兩類ASSERT系列和EXPECT系列。
TEST(StringCmpTest, Demo)
{
EXPECT_EQ(3, add(1, 2));
ASSERT_EQ(3, add(1, 2));
}
我們再來看下所支援的巨集
- ASSERT_EQ
- ASSERT_NE
- ASSERT_LE
- ASSERT_LT
- ASSERT_GE
- ASSERT_GT
- EXPECT_EQ
- EXPECT_NE
- EXPECT_LE
- EXPECT_LT
- EXPECT_GE
- EXPECT_GT
- ASSERT_TRUE
- ASSERT_FALSE
- ASSERT_STREQ
- ASSERT_STRNE
- ASSERT_STRCASEEQ
- ASSERT_STRCASENE
- ASSERT_FLOAT_EQ
- ASSERT_DOUBLE_EQ
直接返回成功還是失敗
- FAIL();
- ADD_FAILURE();
Predicate Assertions
在使用EXPECT_TRUE或ASSERT_TRUE時,有時希望能夠輸出更加詳細的資訊,比如檢查一個函式的返回值TRUE還是FALSE時,希望能夠輸出傳入的引數是什麼,以便失敗後好跟蹤。因此提供瞭如下的斷言:
- ASSERT_PRED1(pred1, val1);
- ASSERT_PRED2(pred2, val1, val2);
如果對這樣的輸出不滿意的話,還可以自定義輸出格式化
- ASSERT_PRED_FORMAT1(pred_format1, val1);`
- ASSERT_PRED_FORMAT2(pred_format2, val1, val2);
例子
如果我們有這樣一個類Arithmetic
我們只需要新建一個ArithmeticUnit.cpp檔案,然後寫下如下程式碼:
#include "stdafx.h"
#include "Arithmetic.h"
#include "gtest/gtest.h"
TEST(Arithmetic, add){
Arithmetic arith;
int a(1), b(2);
EXPECT_EQ(3, arith.add(1, 2));
}
3. 深入解析gTest
首先從TEST巨集入手, 我們看下巨集的定義
//1
define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
//2
#define GTEST_TEST(test_case_name, test_name)\
GTEST_TEST_(test_case_name, test_name, \
::testing::Test, ::testing::internal::GetTestTypeId())
//3
#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
public:\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
private:\
virtual void TestBody();\
static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
GTEST_DISALLOW_COPY_AND_ASSIGN_(\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
....
所以
1. 最終展開的巨集是繼承自testing::Test類
2. 我們最終寫的程式碼是放在TestBody()中的
3. 通過靜態變數test_info_,呼叫MakeAndRegisterTestInfo對測試案例進行註冊。
看下MakeAndRegisterTestInfo 是如何實現的
TestInfo* MakeAndRegisterTestInfo(
const char* test_case_name,
const char* name,
const char* type_param,
const char* value_param,
TypeId fixture_class_id,
SetUpTestCaseFunc set_up_tc,
TearDownTestCaseFunc tear_down_tc,
TestFactoryBase* factory) {
TestInfo* const test_info =
new TestInfo(test_case_name, name, type_param, value_param,
fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
TestInfo物件主要用於包含如下資訊:
- 測試案例名稱(testcase name)
測試名稱(test name)
該案例是否需要執行
- 執行案例時,用於建立Test物件的函式指標
測試結果
我們還看到,TestInfo的建構函式中,非常重要的一個引數就是工廠物件
internal::TestFactoryBase* factory
它主要負責在執行測試案例時建立出Test物件
new ::testing::internal::TestFactoryImpl<\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>)
我們再來看下 TestFactoryImpl 是如何實現的
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
virtual Test* CreateTest() { return new TestClass; }
};
我靠, 這也能算是工廠嗎~
不過總之流程是, 我們要建立一個測試物件的時候,先呼叫factory的CreateTest()方法 建立TestInfo物件, 再通過 GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);對TestInfo物件進行註冊
UnitTest 是單例
UnitTestImpl 是實現
void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
Test::TearDownTestCaseFunc tear_down_tc,
TestInfo * test_info) {
// 獲取或建立了一個TestCase物件,並將testinfo新增到TestCase物件中。
GetTestCase(test_info->test_case_name(),
test_info->test_case_comment(),
set_up_tc,
tear_down_tc)->AddTestInfo(test_info);
}
這裡TestCase物件就出來了
TEST巨集中的兩個引數,第一個引數testcase_name,就是TestCase物件的名稱,第二個引數test_name就是Test物件的名稱。而TestInfo包含了一個測試案例的一系列資訊。
一個TestCase物件對應一個或多個TestInfo物件。
總結一下gtest裡的幾個關鍵的物件:
- UnitTest 單例,總管整個測試,包括測試環境資訊,當前執行狀態等等
- Test 我們自己編寫的,或通過TEST,TEST_F等巨集展開後的Test物件,管理著測試案例的前後事件,具體的執行程式碼TestBody。
- TestCase 測試案例物件,管理著基於TestCase的前後事件,管理內部多個TestInfo。
- TestInfo 管理著測試案例的基本資訊,包括Test物件的建立方法。
一個簡單的UML圖如下