googleTest 源码主线框架性分析——TDD 01

TDD,测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。

简言之TDD是通过设计 Test 来完成软件设计的一种高效可行的软件开发模式。

为何更丰富地达成测试的目的,googletes是绕不过去的,本文备忘主要关注 googletest 主体的分析过程和结论,即,googleTest框架中是如何通过相关的测试宏的,实现测试的目的。

TEST TEST_F TEST_P 等等

1,googleTest 环境与简单示例

1.1 下载 googletest 并编译

下载:

$ git clone https://github.com/google/googletest.git
$ git checkout release-1.10.0

编译:

$ mkdir build
$ cd build/
$ export CXXFLAGS="-Wno-error=maybe-uninitialized"
$ cmake ..
$ make -j
$ ls lib/

 默认为 release,若debug版本则须:

$ cmake .. -DCMAKE_BUILD_TYPE=Debug

成果:

1.2 示例1 验证函数 add

源码

#include <iostream>
#include "gtest/gtest.h"int add_int_int(int a, int b){return a+b;
}TEST(SumFuncTest, twoNumbers){EXPECT_EQ(add_int_int(3,4),7);EXPECT_EQ(27, add_int_int(9, 18));
}GTEST_API_ int main(int argc, char** argv) {printf("Running main() from %s\n", __FILE__);testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行:

1.3 示例 2

#include <gtest/gtest.h>int Foo(int a, int b)
{if (a == 0 || b == 0){throw "don't do that";}int c = a % b;if (c == 0)return b;return Foo(b, c);
}TEST(FooTest, HandleNoneZeroInput)
{EXPECT_EQ(2, Foo(4, 10));EXPECT_EQ(6, Foo(30, 18));
}

g++ foo.cpp -I ../../googletest/googletest/include -L ../../googletest/build_dbg/lib -lgtest -lgtest_main

编译运行:

1.4 示例3

源码:

#include <iostream>
#include "gtest/gtest.h"// add_util.ccfloat add_from_left(float a, float b, float c, float d,	float e)
{float sum = 0.0;sum += c;sum += a;sum += b;//sum += c;sum += d;sum += e;
/*sum += a;sum += b;sum += c;sum += d;sum += e;
*/printf("add_from_left: sum = %f\n", sum);return sum;
}float add_from_right(float a, float b, float c,	float d, float e)
{float sum = 0.0;sum += e;sum += d;sum += c;sum += b;sum += a;printf("add_from_right: sum = %f\n", sum);return sum;
}int sum(int a, int b){return a+b;
}TEST(AddFuncTest, floatVSfloat) {printf("AddFuncTest: float  sum = %f\n", 1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f);printf("AddFuncTest: double sum = %f\n", 12.23209 + 7898.3 + 0.000353265 + 3.7 + 1.238);EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_left(1.238, 3.7, 0.000353265, 7898.3, 12.23209));EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_right(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
//
}TEST(AddFuncTest, doubleVSfloat) {printf("AddFuncTest: float  sum = %f\n", 1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f);printf("AddFuncTest: double sum = %f\n", 12.23209 + 7898.3 + 0.000353265 + 3.7 + 1.238);EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_left(1.238, 3.7, 0.000353265, 7898.3, 12.23209));EXPECT_EQ(1.238 + 3.7 + 0.000353265 + 7898.3 + 12.23209, add_from_right(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
//
}TEST(SumFuncTest, twoNumbers){EXPECT_EQ(sum(3,4),7);EXPECT_EQ(27, sum(9, 18));
}GTEST_API_ int main(int argc, char** argv) {printf("Running main() from %s\n", __FILE__);testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

Makefile

EXE := hello_gtest_ex hello_gtest_add_int_int
all: $(EXE)%: %.cppg++ -O0 -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)INC := -I../googletest/googletest/include/
LD_FLAGS := -L../googletest/build/lib/ -lgtest -lgtest_main.PHONY: clean
clean:-rm -rf $(EXE)

2,示例与源码分析

使用最简单的测试示例,聚焦googletest本身的代码逻辑

观察点,main 函数如何调用到 TEST(...){...} 这种结构中的代码

两种方式互相印证:

方式1,通过编译器的预编译指令 g++ -E ... 生成展开代码;

方式2,通过跟踪源代码,来份些TEST等的展开结果

2.1 TEST

示例代码如上:

simple_gtest.cpp


#include "gtest/gtest.h"int add_int_int(int a, int b){return a+b;
}TEST(SumFuncTest, twoNumbers){EXPECT_EQ(add_int_int(3,4),7);
}

方式1:

g++ -E simple_gtest.cpp -o simple_gtest.i

展开后,simple_gtest.i文件有8W多行,但是其中对我们理解有意义的也就最尾巴上的几行:


int add_int_int(int a, int b){return a+b;
}static_assert(sizeof("SumFuncTest") > 1, "test_suite_name must not be empty"); 
static_assert(sizeof("twoNumbers") > 1, "test_name must not be empty"); class SumFuncTest_twoNumbers_Test : public ::testing::Test { 
public: SumFuncTest_twoNumbers_Test() {} 
private:virtual void TestBody(); static ::testing::TestInfo* const test_info_ __attribute__ ((unused)); SumFuncTest_twoNumbers_Test(SumFuncTest_twoNumbers_Test const &) = delete; void operator=(SumFuncTest_twoNumbers_Test const &) = delete; 
}; ::testing::TestInfo* const SumFuncTest_twoNumbers_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "SumFuncTest", "twoNumbers", nullptr, nullptr, ::testing::internal::CodeLocation("simple_gtest.cpp", 8), (::testing::internal::GetTestTypeId()), ::testing::internal::SuiteApiResolver< ::testing::Test>::GetSetUpCaseOrSuite("simple_gtest.cpp", 8), ::testing::internal::SuiteApiResolver< ::testing::Test>::GetTearDownCaseOrSuite("simple_gtest.cpp", 8), new ::testing::internal::TestFactoryImpl<SumFuncTest_twoNumbers_Test>); void SumFuncTest_twoNumbers_Test::TestBody()
{switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("add_int_int(3,4)", "7", add_int_int(3,4), 7))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "simple_gtest.cpp", 9, gtest_ar.failure_message()) = ::testing::Message();
}

分析这段代码会发现,

TEST被展开成为了一个 class SumFuncTest_twoNumbers_Test

它有一个成员函数 TestBody(){....}

观察上述代码中最后一个函数体:void SumFuncTest_twoNumbers_Test::TestBody()

其中出现了被测试的函数等。

这说明,这个函数体中的代码才是是被测试内容,而其外围都是框架。

框架部分只需要把这中类的一个实例添加到某个链表中,然后依次迭代执行每个类的 TestBody成员函数,既可以完成测试任务。

本例中的 class 如下:

通过方法2.来验证一下展开的结果:

第一部分,class 宏

关联TEST宏,我们可以找到如下内容:


#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)#define GTEST_TEST(test_suite_name, test_name)             \GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \::testing::internal::GetTestTypeId())// Expands to the name of the class that implements the given test.
#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \test_suite_name##_##test_name##_Test// Helper macro for defining tests.
#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id)       \static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1,                 \"test_suite_name must not be empty");                          \static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1,                       \"test_name must not be empty");                                \class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \: public parent_class {                                                  \public:                                                                     \GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;            \~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default;  \GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \(const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete;     \GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \const GTEST_TEST_CLASS_NAME_(test_suite_name,                          \test_name) &) = delete; /* NOLINT */      \GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \(GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \GTEST_TEST_CLASS_NAME_(test_suite_name,                                \test_name) &&) noexcept = delete; /* NOLINT */  \\private:                                                                    \void TestBody() override;                                                  \static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;      \};                                                                           \\::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,           \test_name)::test_info_ =   \::testing::internal::MakeAndRegisterTestInfo(                            \#test_suite_name, #test_name, nullptr, nullptr,                      \::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id),  \::testing::internal::SuiteApiResolver<                               \parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),          \::testing::internal::SuiteApiResolver<                               \parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),       \new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(     \test_suite_name, test_name)>);                                   \void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()

其中的如下两行:

class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \

: public parent_class { \

TEST 的宏充分展开后,会根据TEST(X,Y) 括号中的X、Y字串定义一个完整的类,并且包含成员函数:
  TestBody()

但是展开的内容中,没有这个函数的函数体。

这个函数体正好就是TEST(X,Y){Z} 中,{Z}的这个部分,即,
 
  TestBody(){Z}

只需要在整个测试系统中,讲上面展开生成的class的一个实例,insert进一个链表中,并依次迭代执行链表的每一对象的成员函数 TestBody(){Z},即可达到测试目的。

第二部分,函数体中的宏

关于 EXPECT_EQ,我们会发现如下定义:

#define EXPECT_EQ(val1, val2) \EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)

而其中的 EXPECT_PRED_FORMAT2 又被展开为如下:

// Binary predicate assertion macros.
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)

又 GTEST_PRED_FORMAT2_ 被定义为:

#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)

而且其中的 GTEST_ASSERT_  被展开为:

#define GTEST_ASSERT_(expression, on_failure)                   \GTEST_AMBIGUOUS_ELSE_BLOCKER_                                 \if (const ::testing::AssertionResult gtest_ar = (expression)) \;                                                           \else                                                          \on_failure(gtest_ar.failure_message())

其中 GTEST_AMBIGUOUS_ELSE_BLOCKER_ 展开为:

#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \switch (0)                          \case 0:                             \default:  // NOLINT

于是得到函数体为:

总之,只需要调用这个 TestBody() 函数,即可完成测试任务。

2.2 TEST_F

2.2.1 小示例编译与运行

保持关于 TEST 宏分析的记忆,我们以一个简单的示例来分析 TEST_F 宏,

#include <gtest/gtest.h>class SampleTestWithFixture : public ::testing::Test {
protected:void SetUp() override {a_ = 1;b_ = 2;}int a_;int b_;
};TEST_F(SampleTestWithFixture, Case2) {a_ = 3;EXPECT_EQ(a_ + b_, 5);
}

Makefile:

EXE := hello_gtest_f
all: $(EXE)%: %.cppg++ -g -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)# g++ -E hello_gtest_f.cpp  -I ../googletest/googletest/include/ -o hello_gtest_f.i
#g++ -g -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)INC := -I../../googletest/googletest/include/
LD_FLAGS := -L../../googletest/build_dbg/lib/ -lgtest -lgtest_main.PHONY: clean
clean:-rm -rf $(EXE)

编译,确保能够正确运行:

$ make

$ ./hello_gtest_f

2.2.2 TEST_F 宏展开分析

$ g++ -E hello_gtest_f.cpp  -I ../googletest/googletest/include/ -o hello_gtest_f.i

生成的预处理后的文件 hello_gtest_f.i 主要内容还是在文件的尾巴上,摘录调整格式如下:

class SampleTestWithFixture : public ::testing::Test {
protected:void SetUp() override {a_ = 1;b_ = 2;}int a_;int b_;
};static_assert(sizeof("SampleTestWithFixture") > 1, "test_suite_name must not be empty");
static_assert(sizeof("Case2") > 1, "test_name must not be empty");class SampleTestWithFixture_Case2_Test : public SampleTestWithFixture{
public:SampleTestWithFixture_Case2_Test() = default;~SampleTestWithFixture_Case2_Test() override = default;SampleTestWithFixture_Case2_Test(const SampleTestWithFixture_Case2_Test&) = delete;SampleTestWithFixture_Case2_Test& operator=( const SampleTestWithFixture_Case2_Test&) = delete;SampleTestWithFixture_Case2_Test(SampleTestWithFixture_Case2_Test &&) noexcept = delete;SampleTestWithFixture_Case2_Test& operator=(SampleTestWithFixture_Case2_Test &&) noexcept = delete;private: void TestBody() override;static ::testing::TestInfo* const test_info_ __attribute__((unused));
};::testing::TestInfo* const SampleTestWithFixture_Case2_Test::test_info_=::testing::internal::MakeAndRegisterTestInfo("SampleTestWithFixture","Case2",nullptr,nullptr,::testing::internal::CodeLocation("hello_gtest_f.cpp", 27),(::testing::internal::GetTypeId<SampleTestWithFixture>()),::testing::internal::SuiteApiResolver< SampleTestWithFixture>::GetSetUpCaseOrSuite("hello_gtest_f.cpp", 27),::testing::internal::SuiteApiResolver< SampleTestWithFixture>::GetTearDownCaseOrSuite("hello_gtest_f.cpp", 27),new ::testing::internal::TestFactoryImpl<SampleTestWithFixture_Case2_Test>);void SampleTestWithFixture_Case2_Test::TestBody()
{a_ = 3;switch (0)case 0:default:if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare( "a_ + b_" ,  "5" ,  a_ + b_ ,  5 )));else::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "hello_gtest_f.cpp", 29, gtest_ar.failure_message())= ::testing::Message();
}

跟 TEST 宏的展开类似,组合 TEST_F(X,Y) 的两个参数,构成一个新的类

class X_Y_Test :public SampleTestWithFixture{

...

... TestBody()

}

宏展开的新类中也有一个成员函数 TestBody();

其中 SampleTestWithFixture 是自己定义的类,会被 X_Y_Test 类共有继承走。

而 TEST_F(...){body} 的类似函数体的部分 {body},也同样被安排成为了 TestBody函数的函数体。

接下来,gtest框架会通过成员 X_Y_Test::test_info_ 的静态赋值过程,将本测试用例挂进系统的代运行链表,届时依次迭代 调用 X_Y_Test::TestBody(); 实现测试感兴趣代码的目的。

2.2.3 总结 TEST_F

TEST_F的意图:

TEST_F的目的是为了把关系密切的测试问题汇总到一个class中来进行测试,可以共用同一个类的对象的上下文成员数据。

TEST_F 中,成员函数的执行顺序:

那么,成员函数 Setup( ) 在什么时候执行呢?

先说答案:

  1      X_Y_Test() 构造函数;//c++ 语法

  2      Setup();                      //数据预备,资源申请

  3      TestBody();                //测试部分

  4      TearDown();               //资源释放

  5      X_Y_Test() 析构函数;//c++ 语法

改造刚才的示例:

#include <gtest/gtest.h>class SampleTestWithFixture : public ::testing::Test {
public:
SampleTestWithFixture(){std::cout<<"construct_STWF"<<std::endl;}
~SampleTestWithFixture(){std::cout<<"destruct_STWF"<<std::endl;}
protected:void SetUp() override {std::cout <<"Hello setupupup()000"<<std::endl;a_ = 1;b_ = 2;std::cout <<"Hello setupupup()111"<<std::endl;}void TearDown() override {std::cout <<"Hello teardownnn()000"<<std::endl;a_ = 4;b_ = 5;std::cout <<"Hello teardownnn()111"<<std::endl;}int a_;int b_;
};TEST_F(SampleTestWithFixture, Case2) {std::cout <<"test_f Casess222"<<std::endl;a_ = 3;EXPECT_EQ(a_ + b_, 5);
}

编译运行:

TEST_F函数体中的部分的一些宏,跟TEST中的一样,展开成为一些比较语句。

2.3 TEST_P

2.3.1 可运行示例

#include "gtest/gtest.h"namespace TTT
{
namespace testing
{
int g_env_switch = 0;class BasicTestFixture : public ::testing::TestWithParam<int>
{
public:BasicTestFixture() {}void SetUp(){g_env_switch = GetParam();std::cout<<"BTF_SetUp() ges="<<g_env_switch<<std::endl;}void TearDown(){}
};#define OK 0
#define FAIL 1int envCheckFunc(void)
{if(g_env_switch > 0) {return OK;}else {return FAIL;}
}TEST_P(BasicTestFixture, BasicTest)
{ASSERT_EQ(envCheckFunc(), OK);
}INSTANTIATE_TEST_SUITE_P(configSwitch, BasicTestFixture, ::testing::Values(1, 0));
//INSTANTIATE_TEST_SUITE_P(failSwitch, BasicTestFixture, ::testing::Values(2, 3));}
}

Makefile:

EXE := hello_gtest_pall: $(EXE)%: %.cppg++ -g -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)# g++ -E hello_gtest_p.cpp  -I ../googletest/googletest/include/ -o hello_gtest_p.i
#g++ -g -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)INC := -I../../googletest/googletest/include/
LD_FLAGS := -L../../googletest/build_dbg/lib/ -lgtest -lgtest_main.PHONY: clean
clean:-rm -rf $(EXE)

编译执行:

因为故意藏了一个逻辑错误,所以第二个参数时,会测试失败:

2.3.2 无实例化宏的预编译

注释掉程序中的所有 INSTANTIATE_TEST_SUITE_P 的行,不厌其烦地再贴一次:

#include "gtest/gtest.h"namespace TTT
{
namespace testing
{
int g_env_switch = 0;class BasicTestFixture : public ::testing::TestWithParam<int>
{
public:BasicTestFixture() {}void SetUp(){g_env_switch = GetParam();std::cout<<"BTF_SetUp() ges="<<g_env_switch<<std::endl;}void TearDown(){}
};#define OK 0
#define FAIL 1int envCheckFunc(void)
{if(g_env_switch > 0) {return OK;}else {return FAIL;}
}TEST_P(BasicTestFixture, BasicTest)
{ASSERT_EQ(envCheckFunc(), OK);
}//INSTANTIATE_TEST_SUITE_P(configSwitch, BasicTestFixture, ::testing::Values(1, 0));
//INSTANTIATE_TEST_SUITE_P(failSwitch, BasicTestFixture, ::testing::Values(2, 3));}
}

g++ -E hello_gtest_p.cpp -I ../googletest/googletest/include/ -o hello_gtest_p.i

这时候预编译后生成的代码如下:

namespace TTT
{
namespace testing
{
int g_env_switch = 0;class BasicTestFixture : public ::testing::TestWithParam<int>
{
public:BasicTestFixture() {}void SetUp(){g_env_switch = GetParam();std::cout<<"BTF_SetUp() ges="<<g_env_switch<<std::endl;}void TearDown(){}
};int envCheckFunc(void)
{if(g_env_switch > 0) {return 0;}else {return 1;}
}class BasicTestFixture_BasicTest_Test : public BasicTestFixture, private ::testing::internal::GTestNonCopyable {
public:BasicTestFixture_BasicTest_Test() {}void TestBody() override;
private:static int AddToRegistry(){::testing::UnitTest::GetInstance()->parameterized_test_registry().GetTestSuitePatternHolder<BasicTestFixture>( "BasicTestFixture", ::testing::internal::CodeLocation("hello_gtest_p.cpp", 35))->AddTestPattern( "BasicTestFixture","BasicTest",new ::testing::internal::TestMetaFactory<BasicTestFixture_BasicTest_Test>(),::testing::internal::CodeLocation("hello_gtest_p.cpp", 35));return 0;}static int gtest_registering_dummy_ __attribute__((unused));
};int BasicTestFixture_BasicTest_Test::gtest_registering_dummy_ = BasicTestFixture_BasicTest_Test::AddToRegistry();void BasicTestFixture_BasicTest_Test::TestBody()
{switch (0)case 0:default:if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("envCheckFunc()", "0", envCheckFunc(), 0)));elsereturn ::testing::internal::AssertHelper(::testing::TestPartResult::kFatalFailure, "hello_gtest_p.cpp", 37, gtest_ar.failure_message()) = ::testing::Message();
}

没有实例化宏 的代码,展开到被测试类的 TestBody()函数后就停止了,如上代码中最后一个函数的定义。

2.3.3 有实例化宏的预编译

多了这两句:

INSTANTIATE_TEST_SUITE_P(configSwitch, BasicTestFixture, ::testing::Values(1, 0));

INSTANTIATE_TEST_SUITE_P(failSwitch, BasicTestFixture, ::testing::Values(2, 3));


namespace TTT
{
namespace testing
{
int g_env_switch = 0;class BasicTestFixture : public ::testing::TestWithParam<int>
{
public:BasicTestFixture() {}void SetUp(){g_env_switch = GetParam();std::cout<<"BTF_SetUp() ges="<<g_env_switch<<std::endl;}void TearDown(){}
};int envCheckFunc(void)
{if(g_env_switch > 0) {return 0;}else {return 1;}
}class BasicTestFixture_BasicTest_Test : public BasicTestFixture, private ::testing::internal::GTestNonCopyable {
public:BasicTestFixture_BasicTest_Test() {}void TestBody() override;private:static int AddToRegistry(){::testing::UnitTest::GetInstance()->parameterized_test_registry().GetTestSuitePatternHolder<BasicTestFixture>( "BasicTestFixture", ::testing::internal::CodeLocation("hello_gtest_p.cpp", 35))->AddTestPattern( "BasicTestFixture","BasicTest",new ::testing::internal::TestMetaFactory<BasicTestFixture_BasicTest_Test>(),::testing::internal::CodeLocation("hello_gtest_p.cpp",35));return 0;}static int gtest_registering_dummy_ __attribute__((unused));
};int BasicTestFixture_BasicTest_Test::gtest_registering_dummy_ = BasicTestFixture_BasicTest_Test::AddToRegistry();
void BasicTestFixture_BasicTest_Test::TestBody()
{switch (0)case 0:default:if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare( "envCheckFunc()", "0", envCheckFunc(), 0)));elsereturn ::testing::internal::AssertHelper(::testing::TestPartResult::kFatalFailure, "hello_gtest_p.cpp", 37, gtest_ar.failure_message()) = ::testing::Message();
}
//TEST_P(X, Y, ...){}
// 跟 TEST_F的展开比较接近,同样是将 TEST_P(){...} 的{...} 部分,作为TestBody的函数体;
// 但,一个重要不懂的地方在于,TEST_P 展开为被测试模式或模版,真正注册近被执行队列,是INSTANTIATE_TEST_SUITE_P的工作。//INSTANTIATE_TEST_SUITE_P(X, Y, ...)的展开位三个函数
//参数容器函数:gtest_XY_EvaluGenerator_    测试参数值;
//被测试类函数:gtest_XY_EvalGenerateName_  测试参数类型;
//测试类压栈函数:gtest_XY_dummy_           将测试类实例化后 push进带测试队列中,通过调用 AddTestSuiteInstantiation//展开,INSTANTIATE_TEST_SUITE_P(configSwitch, BasicTestFixture, ::testing::Values(1, 0));static ::testing::internal::ParamGenerator<BasicTestFixture::ParamType> gtest_configSwitchBasicTestFixture_EvalGenerator_()
{return::testing::Values(1, 0);
}static ::std::string gtest_configSwitchBasicTestFixture_EvalGenerateName_( const ::testing::TestParamInfo<BasicTestFixture::ParamType>& info)
{if (::testing::internal::AlwaysFalse()) {::testing::internal::TestNotEmpty(::testing::internal::DefaultParamName<BasicTestFixture::ParamType>);auto t = std::make_tuple(::testing::Values(1, 0));static_assert(std::tuple_size<decltype(t)>::value <= 2, "Too Many Args!");}return ((::testing::internal::DefaultParamName<BasicTestFixture::ParamType>))(info);
}static int gtest_configSwitchBasicTestFixture_dummy_ __attribute__((unused))= ::testing::UnitTest::GetInstance()->parameterized_test_registry().GetTestSuitePatternHolder<BasicTestFixture>("BasicTestFixture", ::testing::internal::CodeLocation("hello_gtest_p.cpp", 40))->AddTestSuiteInstantiation("configSwitch",&gtest_configSwitchBasicTestFixture_EvalGenerator_,&gtest_configSwitchBasicTestFixture_EvalGenerateName_,"hello_gtest_p.cpp",40);//展开,INSTANTIATE_TEST_SUITE_P(failSwitch, BasicTestFixture, ::testing::Values(2, 3));static ::testing::internal::ParamGenerator<BasicTestFixture::ParamType> gtest_failSwitchBasicTestFixture_EvalGenerator_()
{return::testing::Values(2, 3);
}static ::std::string gtest_failSwitchBasicTestFixture_EvalGenerateName_( const ::testing::TestParamInfo<BasicTestFixture::ParamType>& info)
{if (::testing::internal::AlwaysFalse()) {::testing::internal::TestNotEmpty(::testing::internal::DefaultParamName<BasicTestFixture::ParamType>);auto t = std::make_tuple(::testing::Values(2, 3));static_assert(std::tuple_size<decltype(t)>::value <= 2, "Too Many Args!");}return ((::testing::internal::DefaultParamName<BasicTestFixture::ParamType>))(info);
}static int gtest_failSwitchBasicTestFixture_dummy_ __attribute__((unused))= ::testing::UnitTest::GetInstance()->parameterized_test_registry().GetTestSuitePatternHolder<BasicTestFixture>("BasicTestFixture", ::testing::internal::CodeLocation("hello_gtest_p.cpp", 41))->AddTestSuiteInstantiation("failSwitch",&gtest_failSwitchBasicTestFixture_EvalGenerator_,&gtest_failSwitchBasicTestFixture_EvalGenerateName_,"hello_gtest_p.cpp",41);}
}

2.3.4 分析 TEST_P 和

TEST_P(X, Y, ...){...}的展开 跟 TEST_F的展开比较接近,继承用户自定义类后,定义一个新类,含有成员函数 TestBody,同样是将 TEST_P(){...} 的{...} 部分,作为TestBody的函数体;

但,一个重要不同的地方在于,TEST_P 展开为被测试模式或模版,真正注册近被执行队列,是INSTANTIATE_TEST_SUITE_P的工作。

INSTANTIATE_TEST_SUITE_P(X, Y, ...)的展开位三个函数

1,参数容器函数:gtest_XY_EvaluGenerator_ 测试参数值;

2,被测试类函数:gtest_XY_EvalGenerateName_ 测试参数类型;

3,测试类压栈函数:gtest_XY_dummy_ 将测试类实例化后 push进带测试队列中,通过调用 AddTestSuiteInstantiation,压栈入队,等待main函数依次迭代调用各个类实例的 TestBody()成员函数,完成测试任务。

而其中的SetUp(), TearDown()等成员函数的调用,跟TEST_F的调用时机相同。

3. 总结

掌握 googletest的使用,
首先需要理解,TESTXXX(){...}结构中,{...}会被展开为一个类的成员函数 TestBody(){...} 的函数体。
其次,掌握Setup,TearDown的调用时机;
然后,对各种判别宏有一定掌握,比如 EXPECT_EQ;
最后,掌握 TEST_P de 参数的各种形式的使用方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/49343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苹果和乔布斯的传奇故事,从车库创业到万亿市值巨头

苹果公司的品牌故事&#xff0c;就像一部充满创新、挑战与辉煌的科幻大片&#xff0c;让人目不暇接。 故事始于1976年&#xff0c;那时&#xff0c;年轻的史蒂夫乔布斯与斯蒂夫沃兹尼亚克在加州的一个简陋车库里&#xff0c;用他们的热情和智慧&#xff0c;捣鼓出了世界上第一…

python学习之闭包与装饰器

一、闭包 闭包允许一个函数访问并操作函数外部的变量&#xff08;即父级作用域中的变量&#xff09;&#xff0c;即使在该函数外部执行。 特性&#xff1a; (1)外部函数嵌套内部函数。 (2)外部函数可以返回内部函数。 (3)内部函数可以访问外部函数的局部变量。 def out()…

linux中使用docker安装mongodb

随着容器的普及&#xff0c;越来越多服务都喜欢跑在容器中&#xff0c;并且安装也很方便快捷&#xff0c;接下来一起看下linux中使用docker来安装mongodb吧&#xff01; 1.首先安装docker&#xff1b; 使用Yum 进行安装&#xff0c;我安装docker比较喜欢参考阿里云中的安装步骤…

通过泛型+函数式编程封装成通用解决方案|缓存穿透、缓存击穿,缓存雪崩

缓存更新方法封装 用到了泛型、函数式编程。 使用函数式编程是因为我们这个是一个通用的工具&#xff0c;使用泛型&#xff08;泛型&#xff08;Generics&#xff09; 允许我们定义类、接口和方法&#xff0c;可以使用不同类型的参数进行操作&#xff09;可以实现数据类型的通…

Mem0 - 个人 AI 的内存层

文章目录 一、关于 Mem0核心功能&#x1f511;路线图 &#x1f5fa;️常见用例Mem0与RAG有何不同&#xff1f; 二、快速入门 &#x1f680;1、安装2、基本用法&#xff08;开源&#xff09;3、高级用法&#x1f527;4、大模型支持 三、MultiOn1、概览2、设置和配置4、将记忆添加…

鸿蒙仓颉语言【模块module】

module 模块 模块配置文件&#xff0c;这里指项目的modules.json 文件&#xff0c;用于描述代码项目的基础元属性。 {"name": "file name", //当前项目的名称"description": "项目描述", //项目描述"version": "1.0…

视频汇聚平台EasyCVR启动出现报错“cannot open shared object file”的原因排查与解决

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、fmp4等&#xf…

kafka基础介绍

一、为什么使用消息队列 1.使用同步的通信方式来解决多个服务之间的通信 同步的通信方式会存在性能和稳定性的问题。 2.使用异步的通信方式 针对于同步的通信方式来说,异步的方式,可以让上游快速成功,极大提高了系统的吞吐量。而且在分布式系统中,通过下游多个服务的 分布式事…

怎么拼接几张图片为一张?拼接几张图片为一张的四种方法推荐

怎么拼接几张图片为一张&#xff1f;要将几张图片拼接成一张完整的图像&#xff0c;你可以利用现代软件和工具来实现这一操作。这种技术可以帮助你创建更大、更复杂的图像&#xff0c;无论是为了美学上的需要还是为了展示更完整的视觉信息。通过合并多张图片&#xff0c;你可以…

Windows上让Qt支持https请求

一.前言 Qt默认其实支持https的&#xff0c;但需要openssl的支持。所以有时候你代码中写了支持https的请求连接&#xff0c;发现程序可以运行&#xff0c;但到了https请求时会报错&#xff0c;如下&#xff1a; 这就是没有openssl的支持&#xff0c;导致QSslSocket无法进行ht…

关于c#的简单应用三题

#region 输入一个正整数&#xff0c;求1~这个数的阶乘 public static void Factorial(int a) { int result 1; for (int i 1; i < a; i) { result result * i; } Console.WriteLine(result); } #endregion #region 一个游戏&#…

【Vue3】从零开始编写项目

【Vue3】从零开始编写项目 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的…

阶梯-度小满春招算法方向第1批

问题的题面是典型的最长上升子序列问题。求方案数属动态规划问题&#xff0c;可推出以a[i]为最大节点的上升子序列方案数公式 dp[i]{dp[j] , 1<j<i-1&&f[j]1f[i]} &#xff08;f为最大上升子序列&#xff09;。 并且这个方案总数不会超过n&#xff0c;因此也…

C2W1.Assignment.Autocorrect.Part2

理论课&#xff1a;C2W1.Auto-correct 文章目录 3. Combining the edits3.1 Exercise 8.Edit one letter3.2 Exercise 9.Edit two letters3.3 Exercise 10.suggest spelling suggestions 4. Minimum Edit Distance4.1 Dynamic ProgrammingExercise 11Test All-in-one 5. Backt…

javaScrip的学习(一)

目录 引言 一、java和JavaScript的联系 二、js中的弹出框 1.alert弹出框 2.confirm带确认取消的按钮弹框 3.prompt带有提示信息且带有输入框的弹框 4.输出到网页中 ​三、js引入方式 1. 放在script标签中 2.放在外部js文件中 四、执行顺序 五、书写规范 1. 语句结…

python爬虫基础——Webbot库介绍

本文档面向对自动化网页交互、数据抓取和网络自动化任务感兴趣的Python开发者。无论你是初学者还是有经验的开发者&#xff0c;Webbot库都能为你的自动化项目提供强大的支持。 Webbot库概述 Webbot是一个专为Python设计的库&#xff0c;用于简化网页自动化任务。它基于Seleniu…

Hi3751V560_SELinux

Hi3751V560_SELinux setenforce Enforcing setenforce Permissive(或“setenforce 0”) getenforce V560:demo本身的: [ 13.765161] type=1400 audit(1628821512.905:4): avc: denied { read } for pid=1926 comm="system_server" name="ifindex" d…

CCRC-DCO数据合规入表正式落地!全流程操作指南来啦!(业内专家总结)

数据合规已绝非大企业专属&#xff01; 随着《网络安全法》《数据安全法》《个人信息保护法》相继落地&#xff0c;只要企业涉及用户的各种信息&#xff0c;哪怕是中小企业也会面临数据合规的监管&#xff0c;从而产生相关的法律需求。 小到APP对个人信息数据的采集&#xff0c…

【深入C++】map和set的使用

文章目录 C 中的容器分类1. 顺序容器2. 关联容器3. 无序容器4. 容器适配器5. 字符串容器6. 特殊容器 set1.构造函数2.迭代器3.容量相关的成员函数4.修改器类的成员函数5.容器相关操作的成员函数 multiset1.equal_range map1.初始化相关的函数2.迭代器3.容量相关的成员函数4.访问…

AVL树的理解和实现[C++]

文章目录 AVL树AVL树的规则或原理 AVL树的实现1.节点的定义2.功能和接口等的实现默认构造函数&#xff0c;析构函数拷贝构造函数插入搜索打印函数检查是否为平衡树&#xff0c;检查平衡因子旋转 AVL树 AVL树&#xff0c;全称Adelson-Velsky和Landis树&#xff0c;是一种自平衡…