首页 > 其他分享 >google test 之 TEST_F详解

google test 之 TEST_F详解

时间:2023-11-01 16:37:36浏览次数:33  
标签:google name testing TEST internal Test test

一、 基本概念:

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()");
}

标签:google,name,testing,TEST,internal,Test,test
From: https://www.cnblogs.com/tommy-weng/p/17798589.html

相关文章

  • Go开发工程师入门到项目实战 Google架构师联合大厂架构师出品
    01|Go语言课程介绍蔡超Mobvista技术副总裁兼首席架构师,前亚马逊(中国)首席软件架构师本节内容你好,我是蔡超,目前在Mobvista担任技术副总裁兼首席架构师。在加入Mobvista前,我也曾在亚马逊,惠普等公司担任过首席软件架构师。我是从小学四年级开始学习计算机编程的,并一直从事......
  • 关于@Test注解
    @Test注解:JUnit4发布的新特性之一就是支持了@Test注解;@Test的使用是该方法可以不用在main方法中调用就可以测试出运行结果的一种测试工具。一般函数都需要有main方法调用才能执行,方便了开发人员在开发过程中测试代码功能。不过需要注意被测试的方法必须是public修饰的。使用......
  • R:T-Test 计算
    1.不配对#创建两组模拟数据group1<-c(9.536312,37.07379,20.82934,37.20035)group2<-c(3.185522,57.45692,10.12008,57.29765)#进行独立样本t-testresult<-t.test(group1,group2)#打印t-test的结果print(result) 2.配对#创建两组模拟数据before<-......
  • AtCoder Beginner Contest 326 F
    F-RobotRotation一句话不开LL,见祖宗感谢大佬,和洛谷上的题解上面已经将的很清楚了,但是如果你跟我一样一开始看不懂他们的代码,那么这篇可能为你解惑点击查看代码#include<bits/stdc++.h>usingnamespacestd;#defineLLlonglong#defineintLL//LL!map<LL,LL>ma;......
  • AtCoder Beginner Contest(abc) 312
    B-TaKCode难度:⭐题目大意题目定义一种矩阵X:该矩阵的是一个长度为9的由黑白色块组成正方形矩阵;该矩阵的左上角和右下角都是一个3*3的黑色矩阵(总共18个),这两个黑色矩阵外面(边缘不算)包围一圈白色色块(总共14个);现在一个n*m的黑白矩阵,问这个大矩阵中有多少......
  • pytest和allure生成报告
    测试用例:importtimefromselenium.webdriver.supportimportexpected_conditionsasECimportpytestimportyamlfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.waitimportWebDriverWaitresult={}......
  • pytest使用
    安装:pipinstallpytest命名规则:.py文件和文件内方法都以test_开头注意:文件及文件夹命名不要与关键字重复前置与后置函数:module范围:importpytestdemoimportrequests#每一个模块都会执行defsetup_module():print("开始了!!!")#每一个模块都会执行defteard......
  • test_your_nc
    IDA分析。直接nc连接即可。......
  • AtCoder Beginner Contest(abc) 311
    B-VacationTogether难度:⭐题目大意给定n个人的工作计划,'o'表示这天休息,'x'表示工作;请找出一段最长的所有人都休息的连续休息的天数;解题思路数据不大,暴力即可;神秘代码#include<bits/stdc++.h>#defineintlonglong#defineIOSios::sync_with_stdio......
  • 基于Googlenet深度学习网络的矿物质种类识别matlab仿真
    1.算法运行效果图预览   2.算法运行软件版本matlab2022a 3.算法理论概述       VGG在2014年由牛津大学著名研究组vGG(VisualGeometryGroup)提出,斩获该年lmageNet竞赛中LocalizationTask(定位任务)第一名和ClassificationTask(分类任务)第二名。Clas......