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",>est_configSwitchBasicTestFixture_EvalGenerator_,>est_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",>est_failSwitchBasicTestFixture_EvalGenerator_,>est_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 参数的各种形式的使用方式
相关文章:
googleTest 源码主线框架性分析——TDD 01
TDD,测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推…...
Python:对常见报错导致的崩溃的处理
Python的注释: mac用cmd/即可 # 注释内容 代码正常运行会报以0退出,如果是1,则表示代码崩溃 age int(input(Age: )) print(age) 如果输入非数字,程序会崩溃,也就是破坏了程序,终止运行 解决方案…...
linux系统进程占cpu 100%解决步骤
1.查找进程 ps aux 查看指定进程: ps aux | grep process_name2.根据进程查找对应的主进程 pstree -p | grep process_name 3.查看主进程目录并删除 ps -axu | grep process_name rm -rf /usr/bin/2cbbb...
数据传输安全--IPSEC
目录 IPSEC IPSEC可以提供的安全服务 IPSEC 协议簇 两种工作模式 传输模式 隧道模式 两个通信保护协议(两个安全协议) AH(鉴别头协议) 可以提供的安全服务 报头 安全索引参数SPI 序列号 认证数据 AH保护范围 传输模…...
Unity XR Interaction Toolkit的安装(二)
提示:文章有错误的地方,还望诸位大神不吝指教! 文章目录 前言一、安装1.打开unity项目2.打开包管理器(PackageManage)3.导入Input System依赖包4.Interaction Layers unity设置总结 前言 安装前请注意:需要…...
什么是PCB流锡槽焊盘/C型焊盘,如何设计?-捷配笔记
在PCB进行机器组装器件时(如波峰焊),为了防止部分需要二次焊接的元器件的焊盘堵孔,就需要在PCB焊盘上面开个过锡槽,以便过波峰焊时,这些焊锡会流掉。开流锡槽就是在焊盘裸铜(敷锡)部…...
电缆故障精准定位系统
简介 电缆故障精准定位系统应用于35~500kV电压等级电缆线路故障精准定位与故障识别。基于百兆高速采样、北斗高精度授时、信号相位误差精确校准等 先进技术的应用,其定位精度小于5米,业内领先。 基于人工智能深度学习算法核心模块可自动、 快速进行故障…...
Google Chrome 浏览器在链接上点右键的快捷键
如今,越来越多的软件都懒得设个快捷键,就算设置了连个下划线也懒得加了。 谷歌浏览器右键 > 链接另存为... 和 复制链接地址 的快捷键 (如图)...
Redis在SpringBoot中遇到的问题:预热,雪崩,击穿,穿透
缓存预热 预热即在产品上线前,先对产品进行访问或者对产品的Redis中存储数据。 原因: 1. 请求数量较高 2. 主从之间数据吞吐量较大,数据同步操作频度较高,因为刚刚启动时,缓存中没有任何数据 解决方法: 1. 使用脚…...
Pytorch 6
罗切斯特回归模型 加了激活函数 加了激活函数之后类 class LogisticRegressionModel(torch.nn.Module):def __init__(self):super(LogisticRegressionModel, self).__init__()self.linear torch.nn.Linear(1,1)def forward(self, x):# y_pred F.sigmoid(self.linear(x))y_p…...
iterator(迭代器模式)
引入 在想显示数组当中所有元素时,我们往往会使用下面的for循环语句来遍历数组 #include <iostream> #include <vector>int main() {std::vector<int> v({ 1, 2, 3 });for (int i 0; i < v.size(); i){std::cout << v[i] << &q…...
使用Web控制端和轻量级客户端构建的开放Web应用防火墙(OpenWAF)
目录 1. 简介2. 项目结构3. Web控制端3.1. 功能概述3.2. 审计(攻击)日志查看3.3. 多个WAF的集中监控和操作3.4. 使用socket进行封装3.5. 日志的高效存储和检索(Redis) 4. 轻量级客户端4.1. 功能概述4.2. 对Web程序的防护4.3. 网络…...
设计模式在FileBrowser中的几个应用
设计模式是代码重构的最终目标,在程序设计中有效的运用这项技术,可以大大提高代码的可读性和可维护性。使整个程序设计结构趋向精致完美。在我维护的FileBrowser模块中可以针对以下方面 应用相应的模式。 1. 使用策略模式来处理文件夹扫描操作 作为网…...
【JavaEE进阶】——Spring AOP
目录 🚩Spring AOP概述 🚩Spring AOP快速⼊⻔ 🎓引入AOP依赖 🎓编写AOP程序 🚩Spring AOP 详解 🎓Spring AOP核⼼概念 🎓通知类型 🎓PointCut 🎓切⾯优先级 Ord…...
Python - conda使用大全
如何使用Conda? 环境 创建环境 conda create -n spider_env python3.10.11查看环境 conda env listconda info -e激活环境 conda activate spider_env退出环境 conda deactivate删除环境 conda env remove -n spider_env包 导出包 说明:导出当前虚拟…...
ASPICE在汽车软件开发中的作用
ASPICE是一个专门为汽车软件开发过程而设计的评估和改进框架。它基于ISO/IEC 15504标准,为汽车供应商提供了一个评估和改进其软件开发流程的方法。ASPICE的目标是确保软件开发过程的一致性和可预测性,从而提高软件的质量和可靠性。 ASPICE的实施对汽车软…...
亚马逊云科技 re:Inforce 2024中国站大会
亚马逊云科技 re:Inforce 2024中国站大会 - 生成式AI时代的全面安全,将于7月25日本周四在北京富力万丽酒店揭幕...
Lottie:动态动画的魔法棒
文章目录 引言官网链接Lottie 的原理基础使用1. 导出动画2. 引入 Lottie 库3. 加载和播放动画 高级使用1. 动画控制2. 交互性3. 自定义动画例子:交互式按钮动画 优缺点优点缺点 结语 引言 Lottie 是 Airbnb 开源的一个动画库,它允许设计师在 Adobe Afte…...
IPython使用技巧整理
IPython 是一个增强的 Python 交互式 shell,它提供了许多便利的功能,比如自动补全、魔术命令、对象内省等。以下是 IPython 的一些使用技巧和示例,结合您提供的列表数据,我将给出一些相关的使用示例。 1. 自动补全(Tab…...
C#数组复习
一、一维数组 using System.Collections; using System.Collections.Generic; using UnityEngine;public class ShuZu : MonoBehaviour {#region 知识点一 基本概念//数组是存储一组相同类型数据的集合//数组分为 一维、二维、交错数组//一般情况 一维数组 就简称为数组#en…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
