googleTest 源码主线框架性分析

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/diannao/49203.shtml

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

相关文章

go语言day16 runtime包 临界资源 sync包

深入理解Java虚拟机到底是什么_java虚拟机是什么-CSDN博客 Golang-100-Days/Day16-20(Go语言基础进阶)/day17_Go语言并发Goroutine.md at master rubyhan1314/Golang-100-Days GitHub runtime 类似jvm&#xff0c;runtime包也提供了垃圾回收功能&#xff0c;不同的是runtime…

Sleuth(Micrometer) +ZipKin分布式链路追踪的解析以及使用

1、用另一种场景来类比Sleuth和Zipkin的作用 我们来设想一个快递公司的物流追踪系统。设你在网上购买了一本书&#xff0c;当你的订单提交后&#xff0c;后台系统会生成一个唯一的订单号&#xff0c;这个订单号就相当于Sleuth中的Trace ID。你的订单会经过几个主要的处理阶段&…

3.5 查找和排序算法

大纲 算法基础 常用的表示算法的方法 算法的复杂度 查找 顺序查找、二分查找 哈希查找 真题 排序 插入排序 希尔排序 简单选择排序 堆排序 冒泡排序 快速排序 归并排序 基数排序 排序算法总结

黑马程序员2024最新SpringCloud微服务开发与实战 个人学习心得、踩坑、与bug记录Day4 重置版 全网最全最快

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会…

UniCAVE实现融合输出或多屏输出

介绍 CAVE沉浸式系统包含单主机多屏和多主机多屏两种系统架构。此文档主要介绍融合输出与单机多屏输出&#xff0c;这两种方式都属于单主机多屏。均以使用UniCAVE输出到前左右下为例。引用的Package为UniCAVE2019。 融合输出 实现原理 &#xff08;猜测&#xff09;是将多块…

2024春秋杯网络安全联赛夏季赛Crypto(AK)解题思路及用到的软件

2024春秋杯网络安全联赛夏季赛Crypto(AK) 2024春秋杯网络安全联赛夏季赛Crypto解题思路以及用到的软件 所有题用到的软件 1.vm(虚拟机kali)和Ubuntu&#xff0c;正常配置即可B站有很多。 2.Visual Studio Code(里面要配置python&#xff0c;crypto库和Sagemath数学软件系统S…

基于微信小程序+SpringBoot+Vue的青少年科普教学系统平台(带1w+文档)

基于微信小程序SpringBootVue的青少年科普教学系统平台(带1w文档) 基于微信小程序SpringBootVue的青少年科普教学系统平台(带1w文档) 这个工具就是解决上述问题的最好的解决方案。它不仅可以实时完成信息处理&#xff0c;还缩短高校教师成果信息管理流程&#xff0c;使其系统化…

LINUX之MMC子系统分析

目录 1. 概念1.1 MMC卡1.2 SD卡1.3 SDIO 2. 总线协议2.1 协议2.2 一般协议2.3 写数据2.4 读数据2.5 卡模式2.5.1 SD卡模式2.5.2 eMMC模式 2.6 命令2.6.1 命令类2.6.2 详细命令 2.7 应答2.8 寄存器2.8.1 OCR2.8.2 CID2.8.3 CSD2.8.4 RCA2.8.5 扩展CSD 3. 关键结构3.1 struct sdh…

数学建模(7)——Logistic模型

一、马尔萨斯人口模型 import numpy as np import matplotlib.pyplot as plt# 初始人口 N0 100 # 人口增长率 r 0.02 # 时间段&#xff08;年&#xff09; t np.linspace(0, 200, 200)# 马尔萨斯人口模型 N N0 * np.exp(r * t)# 绘图 plt.plot(t, N, labelPopulation) plt.…

【数据结构】堆的实现以及建堆算法和堆排序

【数据结构】堆的实现以及建堆算法和堆排序 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;数据结构 文章目录 【数据结构】堆的实现以及建堆算法和堆排序前言一.堆的实现1.1 堆数据的插入1.2堆数据的删除 二.建堆算法和堆排序2.1思路分析2.…

java的转义字符,注释和代码规范

目录 1.Java运行机制 Java开发快速入门 Java开发注意事项和细节说明 Java学习方法 2.Java的转义字符 Java常用的转义字符 代码示例&#xff1a; 转义字符练习 3.初学java易犯错误 4.注释&#xff08;comment&#xff09; 单行注释与多行注释 文档注释 5.Java代码规…

Spark轨迹大数据处理_scalaSpark代码实两个GIS点(经纬度点)之间的方位角计算

终于有时间来搞一搞这个专栏了 首先声明一下 1、我这个代码是基于一个简化方位角模型&#xff0c;忽略了地球的曲率&#xff0c;适用于距离相对较短的距离。因为业务相关&#xff0c;这个方位角两个点的距离计算不会超过1000km。 2、我这个方位角的计算逻辑&#xff1a;是从一…

使用Top进行设备性能分析思路

Top命令 像windows一样&#xff0c;linux也有一个“进程管理”&#xff0c;可以在命令行执行 top &#xff0c;就可以整体的查看当前机器的资源及进程情况。 在性能问题中&#xff0c;Top是使用较多的一个命令&#xff0c;一般用它可以从整体上了解系统的CPU、内存、IO情况&am…

MongoDB教程(十七):MongoDB主键类型ObjectId

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、Object…

【状态机动态规划 状态压缩】1434. 每个人戴不同帽子的方案数

本文涉及知识点 位运算、状态压缩、枚举子集汇总 动态规划汇总 LeetCode 1434. 每个人戴不同帽子的方案数 总共有 n 个人和 40 种不同的帽子&#xff0c;帽子编号从 1 到 40 。 给你一个整数列表的列表 hats &#xff0c;其中 hats[i] 是第 i 个人所有喜欢帽子的列表。 请你…

建投数据人力资源系列产品获得欧拉操作系统及华为鲲鹏技术认证书

近日&#xff0c;经欧拉生态创新中心和华为技术有限公司测评&#xff0c;建投数据自主研发的人力资源管理系统、招聘管理系统、绩效管理系统、培训管理系统&#xff0c;完成了基于欧拉操作系统openEuler 22.03、华为鲲鹏Kunpeng 920&#xff08;Taisha 200&#xff09;的兼容性…

ASP.NET MVC

ASP.NET MVC与.NET Framework关系 .NET Framework是一个庞大的代码库&#xff0c;能为多种编程语言提供支持(如C#、VB、F#等)。同时.NET Framework 提供了多种技术框架&#xff0c;ASP.NET MVC是.NET Framework提供的众多技术框架中的一种&#xff0c;用于开发Web应用。 .NET …

B端:小小详情页蕴藏大学问,附大量案例 。

在B端&#xff08;Business-to-Business&#xff0c;即企业与企业之间的商业模式&#xff09;的设计中&#xff0c;详情页是一个非常重要的环节。虽然它可能看起来只是一个简单的页面&#xff0c;但实际上其中蕴藏着许多大学问。 用户需求与体验&#xff1a; 在B端设计中&…

【Docker】Docker-consul容器服务自动发现与注册

目录 一.Consul概述 1.解决了什么问题 2.什么叫微服务或者注册与发现 3.consul的模式 4.相关命令 二.consul 部署 1.consul服务器部署 2.部署docker容器 3.Nginx负载均衡器 3.1.安装启动nginx 3.2.配置nginx负载均衡 3.3.创建配置consul complate模板文件 3.4.添加…

基于Qt的上位机通用框架

0.前言 最近一年多的时间一直在开发设备控制相关的软件&#xff0c;加上之前在聚光的两年时间&#xff0c;前前后后开发这种设备控制类型的上位机软件也有三年的时间了。总结出了一套基于Qt的上位机编程框架&#xff0c;核心思想类似于C#的依赖注入&#xff0c;对象的初始化都…