google test 之 TEST_F详解

发布时间 2023-11-01 16:24:05作者: tommy-weng

一、 基本概念:

google test 三种测试用例写法:

  1. TEST(test_suite_name, test_name)

第一种是最基本写法:

#include <gtest/gtest.h>

int add(int a, int b)
{
    return a + b;
}

TEST(testAdd, testArrayAdd)
{
    int a[] = {1,2,3,4,5};
    int b[] = {5,6,7,8,9};
    int c[] = {6,8,10,13,14};
    for (int i{0}; i < 5; i++)
    {
        EXPECT_EQ(c[i], add(a[i], b[i])) << "i = " << i;
    }
}
  1. TEST_F(test_fixture, test_name),这种写法是在testCase和框架中的::testing::Test基类之间增加一个测试夹具类,对各测试用例进行统一的变量声明,定义,初始化等相关操作。
class TestTranscript : public ::testing::Test
{
public:
    void SetUp() override
    {
        mockArithmeticsConstructor = std::make_unique<MockArithmeticsConstructor>();
        mockArithmetics = std::make_shared<MockArithmetics>();
        EXPECT_CALL(*mockArithmeticsConstructor, construct()).WillRepeatedly(Return(mockArithmetics));
        transcript = std::make_shared<Transcript>();
    }

    void TearDown() override
    {
        mockArithmeticsConstructor.reset();
    }
protected:

    std::shared_ptr<Transcript> transcript{nullptr};
    std::shared_ptr<MockArithmetics> mockArithmetics{nullptr};
};

TEST_F(TestTranscript, testCompute)
{
    EXPECT_CALL(*mockArithmetics, plus(Gt(1),Gt(1))).Times(AnyNumber()).WillRepeatedly(Return(98));
    EXPECT_CALL(*mockArithmetics, minus(Lt(100),Le(100))).Times(AnyNumber()).WillRepeatedly(Return(98));
    EXPECT_EQ(98, transcript->compute(98, 99, 100));
}
  1. TEST_P(test_suite_name, test_name), parameterized 参数化测试,可以用一系列参数去执行同样的测试逻辑。解决同样的函数需要写多分拷贝然后输入不同的参数的问题。
int add(int a, int b)
{
    return a + b;
}

struct Params
{
    int a;
    int b;
    int c;
};

class TestAddSuite: public ::testing::TestWithParam<Params>
{};

std::vector<Params> params{{1,2,3},{2,3,5},{3,4,8}};
INSTANTIATE_TEST_CASE_P(aTest, TestAddSuite, ::testing::ValuesIn(params));

TEST_P(TestAddSuite, testadd)
{
    Params params = GetParam();
    EXPECT_EQ(params.c, add(params.a, params.b));
}

要执行测试用例还需要定义一个main函数:

#include <gtest/gtest.h>

int main(int argc, char** argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

编译时需要加上链接库,例如:
g++ TestTranscript.cpp main.cpp -lgtest -lgmock -lpthread

我们的产品代码中使用TEST_F宏最多,所以本文将逐步解析TEST_F如何创建测试用例类和类对象,以及如何run的全过程。(文中列出主流程,省略了很多细节,更多细节详见gtest源码。)

二、 类图:

三、代码详解:

1. TEST_F 宏展开:

#define TEST_F(test_fixture, test_name)\
  GTEST_TEST_(test_fixture, test_name, test_fixture, \
              ::testing::internal::GetTypeId<test_fixture>())

2. GTEST_TEST_ 宏展开:

#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)() {}                   \
                                                                              \
   private:                                                                   \
    virtual void TestBody();                                                  \
    static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;     \
    GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
                                                           test_name));       \
  };                                                                          \
                                                                              \
  ::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 FooTest: public ::testing::Test
{
public:
    void SetUP(){}
    void TearDown{}
}

TEST_F(FooTest, Demo)
{
    EXPECT_EQ(1, 1);
}

TEST_F宏展开后如下所示:

class FooTest_Demo_Test : public FooTest 
{
public: 
    FooTest_Demo_Test() {}
private: 
    virtual void TestBody();
    static ::testing::TestInfo* const test_info_;
    FooTest_Demo_Test(const FooTest_Demo_Test &);
    void operator=(const FooTest_Demo_Test &);
};

::testing::TestInfo* 
    const FooTest_Demo_Test::test_info_ = 
        ::testing::internal::MakeAndRegisterTestInfo( 
            "FooTest", "Demo", "", "",
            (::testing::internal::GetTestTypeId()),
            ::testing::Test::SetUpTestCase,
            ::testing::Test::TearDownTestCase,
            new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>);

void FooTest_Demo_Test::TestBody()
{
    switch (0)
    case 0:
        if (const ::testing::AssertionResult 
                gtest_ar = 
                    (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1", "1", 1, 1)))
            ;
        else 
            ::testing::internal::AssertHelper(
                ::testing::TPRT_NONFATAL_FAILURE,
                ".\\gtest_demo.cpp",
                9,
                gtest_ar.failure_message()
                ) = ::testing::Message();
}

3. test_info对象的创建和注册:

3.1 TestFactoryImpl 工厂类的实现,(注意:这里只是创建了TestFactoryImpl工厂类对象并保存起来,而在run的时候才会调用其中的CreateTest()函数。)

template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
 public:
  Test* CreateTest() override { return new TestClass; }
};

3.2 MakeAndRegisterTestInfo 创建TestInfo,并注册到TestSuite中。

TestInfo* MakeAndRegisterTestInfo(
    const char* test_suite_name, const char* name, const char* type_param,
    const char* value_param, CodeLocation code_location,
    TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
    TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
  TestInfo* const test_info =
      new TestInfo(test_suite_name, name, type_param, value_param,
                   code_location, fixture_class_id, factory);
  GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
  return test_info;
}

3.3 AddTestInfo函数先找到对应的TestSuite,再把TestInfo注册进去。

  void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
                   internal::TearDownTestSuiteFunc tear_down_tc,
                   TestInfo* test_info) {
    if (original_working_dir_.IsEmpty()) {
      original_working_dir_.Set(FilePath::GetCurrentDir());
      GTEST_CHECK_(!original_working_dir_.IsEmpty())
          << "Failed to get the current working directory.";
    }

    GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
                 set_up_tc, tear_down_tc)
        ->AddTestInfo(test_info);
  }

3.4 在TestSuite的链表中插入test_info

void TestSuite::AddTestInfo(TestInfo* test_info) {
  test_info_list_.push_back(test_info);
  test_indices_.push_back(static_cast<int>(test_indices_.size()));
}

4. RUN_ALL_TESTS() 剖析

4.1 RUN_ALL_TESTS() 获取::testing::UnitTest自身的单例,调用run()函数把控制权托管给UnitTestImpl类。

inline int RUN_ALL_TESTS() {
  return ::testing::UnitTest::GetInstance()->Run();
}
int UnitTest::Run() {
  ...
  return internal::HandleExceptionsInMethodIfSupported(
      impl(),
      &internal::UnitTestImpl::RunAllTests,
      "auxiliary test code (environments or event listeners)") ? 0 : 1;
}

4.2 UnitTestImpl的RunAllTests接受托管,执行其中保存的TestSuite中的Run()

bool UnitTestImpl::RunAllTests() {
    ...
    for (int test_index = 0; test_index < total_test_suite_count(); test_index++) {
       GetMutableSuiteCase(test_index)->Run();
    ...
}
void TestSuite::Run() {
  ...
  for (int i = 0; i < total_test_count(); i++) {
    GetMutableTestInfo(i)->Run();
  }
  ...
}

4.3 TestInfo::Run()函数接受TestSuite::run()的调用,执行其中保存TestInfo::run()

void TestInfo::Run() {
  ...
  Test* const test = internal::HandleExceptionsInMethodIfSupported(
      factory_, &internal::TestFactoryBase::CreateTest, // 这里才会真正调用CreateTest()创建TestClass对象,也就是我们定义的TEST_F展开后的内容。
      "the test fixture's constructor");

  if (!Test::HasFatalFailure() && !Test::IsSkipped()) {

    test->Run();
  ...
  }

4.4 最终调到测试用例的Test::TestBody

void Test::Run() {
  if (!HasSameFixtureClass()) return;

  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
  impl->os_stack_trace_getter()->UponLeavingGTest();
  internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");

  if (!HasFatalFailure() && !IsSkipped()) {
    impl->os_stack_trace_getter()->UponLeavingGTest();
    internal::HandleExceptionsInMethodIfSupported(
        this, &Test::TestBody, "the test body"); // 这里调用我们写的Test::TestBody测试内容
  }

  impl->os_stack_trace_getter()->UponLeavingGTest();
  internal::HandleExceptionsInMethodIfSupported(
      this, &Test::TearDown, "TearDown()");
}