Google Test(GTest)使用方法和源码解析——模板类测试技术分析和应用
? ? ? ? 寫C++難免會遇到模板問題,如果要針對一個模板類進行測試,似乎之前博文中介紹的方式只能傻乎乎的一個一個特化類型后再進行測試。其實GTest提供了兩種測試模板類的方法,本文我們將介紹方法的使用,并分析其實現原理。(轉載請指明出于breaksoftware的csdn博客)
應用
? ? ? ? GTest將這兩種方法叫做:Typed Tests和Type-Parameterized Tests。我覺得可能叫做簡單模式、高級模式比較通用。先不管這些名字吧,我們看看怎么使用
簡單模式(Typed Tests)
? ? ? ? 首先我們要定義一個模板類。但是為了使用GTest框架,它要繼承于Test類
template <typename T>
class TypeTest : public Test {
protected:bool CheckData() {if (typeid(T) == typeid(bool)) {return false;}else {return true;}}
};
? ? ? ? 我們只實現了一個返回bool類型的模板類型判斷函數CheckData。
? ? ? ? 然后我們使用下列方式定義一個類型,類型的模板參數是我們需要傳遞給TypeTest的模板類型
typedef testing::Types<int, long> IntegerTypes
? ? ? ? 接下來我們使用TYPED_TEST_CASE宏注冊一個測試用例
TYPED_TEST_CASE(TypeTest, IntegerTypes);
? ? ? ? 最后我們使用TYPED_TEST_P定義一個測試特例
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};
? ? ? ? 如此我們可以對TypeTest<int>和TypeTest<long>進行測試。我們看下結果
[----------] 1 test from TypeTest/0, where TypeParam = int
[ RUN ] TypeTest/0.Verify
[ OK ] TypeTest/0.Verify (2454 ms)
[----------] 1 test from TypeTest/0 (2455 ms total)[----------] 1 test from TypeTest/1, where TypeParam = long
[ RUN ] TypeTest/1.Verify
[ OK ] TypeTest/1.Verify (4843 ms)
[----------] 1 test from TypeTest/1 (11093 ms total)
高級模式
? ? ? ? 如果我們還要對TypeTest使用其他類型作為模板參數進行測試,可能要這么寫
typedef testing::Types<float, double> FloatTypes;
TYPED_TEST_CASE(TypeTest, FloatTypes); // compile error
? ? ? ? 但是編譯會報錯,因為TYPED_TEST_CASE中定義的變量重定義了(之前TYPED_TEST_CASE(TypeTest, IntegerTypes);中定義的)。這個時候我們就要使用高級模式
? ? ? ? 首先我們需要聲明一下測試用例類
TYPED_TEST_CASE_P(TypeTest);
? ? ? ? 然后使用TYPED_TEST_P定義一個測試實體
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};
? ? ? ? 接下來使用REGISTER_TYPED_TEST_CASE_P注冊測試用例
REGISTER_TYPED_TEST_CASE_P(TypeTest, Verify);
? ? ? ? 最后使用INSTANTIATE_TYPED_TEST_CASE_P宏創建每個測試特例
INSTANTIATE_TYPED_TEST_CASE_P(IntegerCheck, TypeTest, IntegerTypes);
INSTANTIATE_TYPED_TEST_CASE_P(FloatCheck, TypeTest, FloatTypes);
INSTANTIATE_TYPED_TEST_CASE_P(BoolCheck, TypeTest, bool);
? ? ? 上面三行測試了三組類型,其輸出是
[----------] 1 test from IntegerCheck/TypeTest/0, where TypeParam = int
[ RUN ] IntegerCheck/TypeTest/0.Verify
[ OK ] IntegerCheck/TypeTest/0.Verify (702 ms)
[----------] 1 test from IntegerCheck/TypeTest/0 (703 ms total)[----------] 1 test from IntegerCheck/TypeTest/1, where TypeParam = long
[ RUN ] IntegerCheck/TypeTest/1.Verify
[ OK ] IntegerCheck/TypeTest/1.Verify (400 ms)
[----------] 1 test from IntegerCheck/TypeTest/1 (403 ms total)[----------] 1 test from FloatCheck/TypeTest/0, where TypeParam = float
[ RUN ] FloatCheck/TypeTest/0.Verify
[ OK ] FloatCheck/TypeTest/0.Verify (451 ms)
[----------] 1 test from FloatCheck/TypeTest/0 (453 ms total)[----------] 1 test from FloatCheck/TypeTest/1, where TypeParam = double
[ RUN ] FloatCheck/TypeTest/1.Verify
[ OK ] FloatCheck/TypeTest/1.Verify (403 ms)
[----------] 1 test from FloatCheck/TypeTest/1 (405 ms total)[----------] 1 test from BoolCheck/TypeTest/0, where TypeParam = bool
[ RUN ] BoolCheck/TypeTest/0.Verify
..\test\gtest_unittest.cc(117): error: Value of: CheckData()Actual: false
Expected: true
[ FAILED ] BoolCheck/TypeTest/0.Verify, where TypeParam = bool (5440 ms)
[----------] 1 test from BoolCheck/TypeTest/0 (6633 ms total)
原理解析
簡單模式
? ? ? ? 我們先從typedef testing::Types<int, long> IntegerTypes;這句開始分析。Types定義非常有意思,我截取部分來看看
struct Types0 {};// Type lists of length 1, 2, 3, and so on.template <typename T1>
struct Types1 {typedef T1 Head;typedef Types0 Tail;
};
template <typename T1, typename T2>
struct Types2 {typedef T1 Head;typedef Types1<T2> Tail;
};template <typename T1, typename T2, typename T3>
struct Types3 {typedef T1 Head;typedef Types2<T2, T3> Tail;
};......template <typename T1 = internal::None, typename T2 = internal::None,typename T3 = internal::None, typename T4 = internal::None,typename T5 = internal::None, typename T6 = internal::None,typename T7 = internal::None, typename T8 = internal::None,typename T9 = internal::None, typename T10 = internal::None,typename T11 = internal::None, typename T12 = internal::None,typename T13 = internal::None, typename T14 = internal::None,typename T15 = internal::None, typename T16 = internal::None,typename T17 = internal::None, typename T18 = internal::None,typename T19 = internal::None, typename T20 = internal::None,typename T21 = internal::None, typename T22 = internal::None,typename T23 = internal::None, typename T24 = internal::None,typename T25 = internal::None, typename T26 = internal::None,typename T27 = internal::None, typename T28 = internal::None,typename T29 = internal::None, typename T30 = internal::None,typename T31 = internal::None, typename T32 = internal::None,typename T33 = internal::None, typename T34 = internal::None,typename T35 = internal::None, typename T36 = internal::None,typename T37 = internal::None, typename T38 = internal::None,typename T39 = internal::None, typename T40 = internal::None,typename T41 = internal::None, typename T42 = internal::None,typename T43 = internal::None, typename T44 = internal::None,typename T45 = internal::None, typename T46 = internal::None,typename T47 = internal::None, typename T48 = internal::None,typename T49 = internal::None, typename T50 = internal::None>
struct Types {typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
};template <>
struct Types<internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None> {typedef internal::Types0 type;
};
template <typename T1>
struct Types<T1, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None> {typedef internal::Types1<T1> type;
};
template <typename T1, typename T2>
struct Types<T1, T2, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None> {typedef internal::Types2<T1, T2> type;
};
? ? ? ? 這段代碼很長,但是其定義了我們用于羅列類型的結構體Types。它是一個遞歸定義,即Types3依賴于Types2,Types2依賴于Types1,Types1依賴于Types0……。每個模板類都會將自己模板列表的第一個模板別名為Head,剩下的類型別名為Tail。未來我們將看到這兩個類型的使用。
? ? ? ? 我們再看下TYPED_TEST_CASE的實現
# define TYPED_TEST_CASE(CaseName, Types) \typedef ::testing::internal::TypeList< Types >::type \GTEST_TYPE_PARAMS_(CaseName)
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
? ? ? ? 它只是對該測試用例的參數列表類的type做了一個別名,展開代碼就是
typedef ::testing::internal::TypeList< IntegerTypes >::type gtest_type_params_TypeTest_
? ? ? ??TypeList是個模板類,它將Types<T>別名為type
template <typename T>
struct TypeList {typedef Types1<T> type;
};
? ? ? ? 對應于我們的例子就是
struct TypeList<IntegerTypes> {typedef Types1<IntegerTypes> type;
};
? ? ? ? 于是整體展開就是
typedef ::testing::internal::Types1<IntegerTypes> gtest_type_params_TypeTest_
? ? ? ? 最后我們看下TYPED_TEST的宏實現。它和之前博文介紹的TEST宏有如下相同之處:
- 定義了私有的虛方法TestBody
- 執行了注冊邏輯
- 末尾聲明了TestBody函數部分,便于開發者填充測試實體
? ? ? ? 相同的地方我們就不說了,我們看下不同的
# define TYPED_TEST(CaseName, TestName) \template <typename gtest_TypeParam_> \class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \: public CaseName<gtest_TypeParam_> { \private: \typedef CaseName<gtest_TypeParam_> TestFixture; \typedef gtest_TypeParam_ TypeParam; \virtual void TestBody(); \}; \
? ? ? ? 它繼承于一個模板類,模板類的類名是我們通過TYPED_TEST傳入的測試用例類。同時它將父類、模板類進行了別名操作。用我們的例子展開代碼即是
template <typename T> class TypeTest_Verify_Test: public TypeTest<T> { private: typedef TypeTest<T> TestFixture; typedef T TypeParam; virtual void TestBody(); };
? ? ? ? 最后一個傀儡變量被初始化
bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \::testing::internal::TypeParameterizedTest< \CaseName, \::testing::internal::TemplateSel< \GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \GTEST_TYPE_PARAMS_(CaseName)>::Register(\"", ::testing::internal::CodeLocation(__FILE__, __LINE__), \#CaseName, #TestName, 0); \
? ? ? ? 我們把代碼展開
bool gtest_TypeTest_Verify_registered_ GTEST_ATTRIBUTE_UNUSED_ =::testing::internal::TypeParameterizedTest<TypeTest, ::testing::internal::TemplateSel<TypeTest_Verify_Test>, gtest_type_params_TypeTest_>\::Register("", ::testing::internal::CodeLocation(__FILE__, __LINE__), 'TypeTest', 'Verify', 0);
? ? ? ? 我們看下TypeParameterizedTest類的Register實現
template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
class TypeParameterizedTest {public:// 'index' is the index of the test in the type list 'Types'// specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,// Types). Valid values for 'index' are [0, N - 1] where N is the// length of Types.static bool Register(const char* prefix,CodeLocation code_location,const char* case_name, const char* test_names,int index) {typedef typename Types::Head Type;typedef Fixture<Type> FixtureClass;typedef typename GTEST_BIND_(TestSel, Type) TestClass;// First, registers the first type-parameterized test in the type// list.MakeAndRegisterTestInfo((std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"+ StreamableToString(index)).c_str(),StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),GetTypeName<Type>().c_str(),NULL, // No value parameter.code_location,GetTypeId<FixtureClass>(),TestClass::SetUpTestCase,TestClass::TearDownTestCase,new TestFactoryImpl<TestClass>);// Next, recurses (at compile time) with the tail of the type list.return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>::Register(prefix, code_location, case_name, test_names, index + 1);}
};
? ? ? ? 這段代碼非常重要,我們看到核心函數MakeAndRegisterTestInfo,它將我們測試的對象加入到框架的執行隊列中。具體它的原理和實現可以參看《Google Test(GTest)使用方法和源碼解析——自動調度機制分析》。
? ? ? ? 第12行別名了Types::Head為Type。Types是傳入的模板類,以我們的例子為例,其傳入的就是::testing::internal::Types1<IntegerTypes>。我們在介紹Types模板類時提到過Head別名,它是該模板類第一個模板參數類型。對應于我們的例子就是typedef testing::Types<int, long> IntegerTypes;中的int類型。
? ? ? ? 第13行使用12行別名的類型,特化了我們傳入的測試用例類,即該行對應于
typedef TypeTest<int> FixtureClass;
? ? ? ? 第14行對測試特例類使用了int類型進行特化
template <GTEST_TEMPLATE_ Tmpl>
struct TemplateSel {template <typename T>struct Bind {typedef Tmpl<T> type;};
};# define GTEST_BIND_(TmplSel, T) \TmplSel::template Bind<T>::type
? ? ? ?即對應于TypeTest_Verify_Test<T>::type
typedef typename TypeTest_Verify_Test<T> TestClass;
? ? ? ? 如此MakeAndRegisterTestInfo函數中的參數就比較明確了。
? ? ? ? 這段代碼中還有個非常重要的一個遞歸調用
// Next, recurses (at compile time) with the tail of the type list.return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>::Register(prefix, code_location, case_name, test_names, index + 1);
? ? ? ? 這次調用,最后一個模板參數傳遞了Types::Tail。它在編譯期間將觸發編譯器進行類型推導,如同抽絲剝繭般,使用typedef testing::Types<int, long> IntegerTypes;中每個模板類型對TypeParameterizedTest::Register進行特化。從而可以在運行期間可以對每個類型進行注冊。
高級模式
? ? ? ? 我們先看下TYPED_TEST_CASE_P宏的實現
# define TYPED_TEST_CASE_P(CaseName) \static ::testing::internal::TypedTestCasePState \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
? ? ? ? 它定義了一個TypedTestCasePState類型的全局變量,對應于我們例子就是
static ::testing::internal::TypedTestCasePState gtest_typed_test_case_p_state_TypeTest_;
? ? ? ?TypedTestCasePState類暴露了AddTestName方法用于保存測試用例和測試特例名? ? ? ? 再看下REGISTER_TYPED_TEST_CASE_P宏的實現
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \} \static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\__FILE__, __LINE__, #__VA_ARGS__)
? ? ? ? 它使用TYPED_TEST_CASE_P定義的TypedTestCasePState類對象方法VerifyRegisteredTestNames獲取已注冊的測試用例名,并別名了一個類型。以上兩個宏都是和測試用例名稱注冊有關。接下來我們看下TYPED_TEST_P的實現
?
# define TYPED_TEST_P(CaseName, TestName) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \template <typename gtest_TypeParam_> \class TestName : public CaseName<gtest_TypeParam_> { \private: \typedef CaseName<gtest_TypeParam_> TestFixture; \typedef gtest_TypeParam_ TypeParam; \virtual void TestBody(); \}; \static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\__FILE__, __LINE__, #CaseName, #TestName); \} \template <typename gtest_TypeParam_> \void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
? ? ? ? 它和我們之前介紹的TYPED_TEST實現是相似的。不同點是:
- 直接使用傳入的測試特例名作為類名
- 調用TYPED_TEST_CASE_P定義的TypedTestCasePState類對象AddTestName對測試用例和測試特例名進行注冊
- 將測試特例類和傀儡變量初始化過程控制在一個和測試用例名相關的命名空間中
? ? ? ? 最后我們看下INSTANTIATE_TYPED_TEST_CASE_P的實現
# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \::testing::internal::TypeParameterizedTestCase<CaseName, \GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \::testing::internal::TypeList< Types >::type>::Register(\#Prefix, \::testing::internal::CodeLocation(__FILE__, __LINE__), \>EST_TYPED_TEST_CASE_P_STATE_(CaseName), \#CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
? ? ? ? 可以說,套路和簡單模式的注冊方式是一樣的。不一樣的是它調用了TypeParameterizedTestCase類的Register,而不是TypeParameterizedTest的Register。還有就是Register的第二個參數是在REGISTER_TYPED_TEST_CASE_P別名的類型。我們看下這個類型的相關代碼
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \} \
// Template lists of length 1, 2, 3, and so on.template <GTEST_TEMPLATE_ T1>
struct Templates1 {typedef TemplateSel<T1> Head;typedef Templates0 Tail;
};
template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
struct Templates2 {typedef TemplateSel<T1> Head;typedef Templates1<T2> Tail;
};
template <>
struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates0 type;
};
template <GTEST_TEMPLATE_ T1>
struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates1<T1> type;
};
template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates2<T1, T2> type;
};
? ? ? ? 可以見得這個類型和之前的Types是類似的,用于在編譯期間通過編譯器推導特例出注冊方法。需要注意的是這個地方推導的不是模板類的類型,而是測試特例類。我們在講解TYPED_TEST_P時提過,宏中直接使用傳入的測試特例名作為類名,這是有原因的。原因就是在這兒要一個個推導。可能這么說還不太明白,我們看下高級模式的另外一種用法
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};TYPED_TEST_P(TypeTest, Verify2) {EXPECT_FALSE(CheckData());
};REGISTER_TYPED_TEST_CASE_P(TypeTest, Verify, Verify2);
? ? ? ? 這兒的Verify和Verify2都是測試特例名,于是通過REGISTER_TYPED_TEST_CASE_P操作后,就變成
typedef ::testing::internal::Templates<Verify,Verfiy2>::type gtest_AllTests_;
? ? ? 最后在下面注冊函數中,觸發對該函數使用Verify和Verfiy2進行特化的操作。
template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
class TypeParameterizedTestCase {public:static bool Register(const char* prefix, CodeLocation code_location,const TypedTestCasePState* state,const char* case_name, const char* test_names) {std::string test_name = StripTrailingSpaces(GetPrefixUntilComma(test_names));if (!state->TestExists(test_name)) {fprintf(stderr, "Failed to get code location for test %s.%s at %s.",case_name, test_name.c_str(),FormatFileLocation(code_location.file.c_str(),code_location.line).c_str());fflush(stderr);posix::Abort();}const CodeLocation& test_location = state->GetCodeLocation(test_name);typedef typename Tests::Head Head;// First, register the first test in 'Test' for each type in 'Types'.TypeParameterizedTest<Fixture, Head, Types>::Register(prefix, test_location, case_name, test_names, 0);// Next, recurses (at compile time) with the tail of the test list.return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>::Register(prefix, code_location, state,case_name, SkipComma(test_names));}
};
? ? ? ? 在TypeParameterizedTestCase類的Register方法中我們看到有對TypeParameterizedTest的Register的調用,于是兩種方式打通了。一個測試特例下的類型推導是在TypeParameterizedTest的Register中完成的,而測試用例下不同測試特例的推導則在TypeParameterizedTestCase類的Register方法中完成的。
總結
以上是生活随笔為你收集整理的Google Test(GTest)使用方法和源码解析——模板类测试技术分析和应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google Test(GTest)使用
- 下一篇: Google Test(GTest)使用