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

news2024/9/23 2:34:11

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.cc

float 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)

%: %.cpp
	g++ -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)

%: %.cpp
	g++ -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 1

int 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_p

all: $(EXE)

%: %.cpp
	g++ -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 1

int 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)))
        ;
      else
        return ::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)))
        ;
      else
        return ::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.coloradmin.cn/o/1944573.html

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

相关文章

上市公司-企业数据要素利用水平(2010-2022年)

企业数据要素利用水平数据&#xff1a;衡量数字化时代企业竞争力的关键指标 在数字化时代&#xff0c;企业对数据的收集、处理、分析和应用能力成为衡量其竞争力和创新能力的重要标准。企业数据要素利用水平的高低直接影响其市场表现和发展潜力。 企业数据要素利用水平的测算…

【保姆级介绍服务器硬件的基础知识】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 🦭服务器硬件基础知识 1. 🦭前言2. 🦭中央处理器(CPU)3. 🦭…

通过POST请求往Elastic批量插入数据

文章目录 引言I 请求文档请求参数请求例子引言 调试工具:Apifox 需求: 向Elasticsearch中的’test_index’索引批量插入文档 情况认证: Basic Auth 在 Header 添加参数 Authorization,其值为在 Basic 之后拼接空格,以及经过 Base64 编码的 {{Username}}:{{Password}} 示…

Effective Javax学习笔记--使可变性最小

这里正式进入第4章“类与接口”&#xff0c;其中第15和16条主要涉及类的封装&#xff0c;相关内容在Code Complete的第六章已经有了较为详细的描述&#xff0c;因此就不再重复了&#xff0c;直接从第17条开始。首先说一下为什么类要保证可变性最小。 为什么要使得类的可变性最…

HackTheBox--Knife

Knife 测试过程 1 信息收集 端口扫描 80端口测试 echo "10.129.63.56 knife.htb" | sudo tee -a /etc/hosts网站是纯静态的&#xff0c;无任何交互功能&#xff0c;检查网页源代码也未发现任何可利用的文件。 检查页面请求时&#xff0c;请求与响应内容&#xff0…

如何避免蓝屏?轻量部署,安全和业务连续性才能两不误

自19日起&#xff0c;因CrowdStrike软件更新的错误配置而导致的“微软全球蓝屏”&#xff0c;影响依然在持续。这场被称为“史上最大规模的IT故障”&#xff0c;由于所涉全球企业太多&#xff0c;专家估计“蓝屏”电脑全部恢复正常仍需时日。 尽管 CEO 乔治 库尔茨&#xff08…

Kali中docker与docker-compose的配置

权限升级 sudo su 升级为root用户 更新软件 apt-get update安装HTTPS协议和CA证书 apt-get install -y apt-transport-https ca-certificates安装pip apt-get install python3-pip查是否成功安装 pip3 -V or pip3 --version 下载docker apt下载docker apt install doc…

【故障排查】Docker启动Nacos报错:No DataSource set 问题解决

Nacos报错内容 Nacos Server did not start because dumpservice bean construction failure : No DataSource set原因分析 Nacos 配置的是单机模式&#xff0c;使用mysql 进行存储配置文件&#xff0c;Nacos的启动脚本已经配置了MySQL的连接方式&#xff0c;根据错误提示&a…

麒麟V10安装nginx、mysql报错缺少包:error while loading shared libraries libssl.so.10

背景 启动nginx报错&#xff1a;error while loading shared libraries libssl.so.10 解决 查看nginx启动文件所依赖的动态链接库&#xff08;即共享库或动态库&#xff09; ldd nginx-1.22.1/sbin/nginx离线安装compat-openssl10包 将依赖包麒麟v10安装openssl10依赖包上…

MYSQL存储引擎InnoDB, MyISAM简介

MYSQL存储引擎 在开始谈到mysql存储引擎之前&#xff0c;我们应该知道或者了解存储引擎是什么&#xff0c;存储引擎是为了解决什么样的问题的。 在mysql中&#xff0c;存储引擎是处理不同表类型SQL操作的MySQL组件&#xff0c;同时MySQL服务器采用可插拔的存储引擎架构&#x…

关于Qt部署CMake导致“Failed to set working directory to”的问题

2024年7月23日补充&#xff1a;该目录过深的情况只在Win10上有发现&#xff0c;Win11则没有问题&#xff0c;且Win11可以在DevHome中设置LongPath。 --------------------------------------------------------------------------------------------------------------- 使用qt…

ABAP+从SAP发出去的PDF文件在第三方系统出现乱码

这是一个 ABAP转换PDF调用函数CALL FUNCTION CONVERT_OTF的问题记录&#xff0c;关乎字体STSong-Light-ldentity-H 和 STSong-Light的区别 背景&#xff1a; 做了一个增强&#xff0c;是采购订单审批后自动发送采购订单PDF1到企业微信&#xff0c;用户再将企业微信收到的P…

【独立站运营经验分享】独立站8大运营模式怎么选?

跨境独立站模式众多&#xff0c;你了解多少呢&#xff1f;今天&#xff0c;带你充分了解各种模式优劣对比&#xff0c;从知道适合做什么样的独立站。 1、铺货模式 指不局限于单一品类&#xff0c;广泛、批量地上架市场反响好的商品模式&#xff0c;以大量的SKU和较低的价格为特…

基于 Electron+Vite+Vue3+Sass 框架搭建

技术参考 技术描述Electron一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。嵌入 Chromium 和 Node.jsElectron Forge用于打包和分发 Electron 应用程序的一体化工具。英文地址在此Vite前端构建工具Vue3用于构建用户界面的 JavaScript 框架vitejs/plugin-vueVite 插…

鸿蒙OS物联网创新应用实训解决方案

摘要&#xff1a; 随着物联网技术的飞速发展&#xff0c;各种智能设备和传感器正在以前所未有的速度融入我们的日常生活。华为推出的鸿蒙操作系统&#xff08;HarmonyOS&#xff09;作为一款面向全场景、多设备、无缝连接的分布式操作系统&#xff0c;为物联网领域带来了全新的…

pdf文件压缩的有效方法,详解5个效果高效的文件压缩方法汇总!

在现代信息社会中&#xff0c;PDF 文件已经成为我们日常工作和学习中不可或缺的重要载体。然而&#xff0c;随着 PDF 文件内容的增多和复杂化&#xff0c;文件大小的膨胀也成为一个常见问题&#xff0c;给存储、共享和传输带来了不少挑战。本文旨在探讨如何通过有效的压缩方法来…

VScode 修改 Markdown Preview Enhanced 字体以及大纲编号

修改字体和背景颜色 按快捷键 Ctrl , 打开设置&#xff0c;搜索 markdown-preview-enhanced.previewTheme&#xff0c;选择一个黑色主题的css&#xff0c;如 github-dark.css. 修改自动编号和背景颜色 背景颜色 按 F1 或者 Ctrl Shift P&#xff0c;输入 Customize CSS…

WEB 手柄 http通信,mcu端解析代码 2024/7/23 日志

WEB 手柄 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>WEB遥控器</title> </head> &l…

【Linux】条件变量及生产者消费者模型

为什么要将这两者放在一起进行呢&#xff1f; 主要是因为生产消费与条件变量关系密切&#xff0c;正好相辅相成。 目录 条件变量&#xff1a;条件变量的引出&#xff1a;条件变量的解释与接口&#xff1a;测试代码&#xff1a; 生产者消费者模型&#xff1a;概念&#xff1a;代…

Window版本nginx修改文件访问句柄数被限制,解决大并发量访问时无法响应问题

目录 一、问题背景 二、问题分析 三、解决办法 四、查看nginx连接状态 一、问题背景 Windows版本因为文件访问句柄数被限制为1024了&#xff0c;当大并发量访问时就会无法响应。会有如下错误提示&#xff1a;maximum number of descriptors supported by select() is 1024…