C++单元测试框架——gtest

C++单元测试框架——gtest——v_raw


gtest简介

gtest是一个跨平台的C++单元测试框架。它提供了丰富的断言、致命和非致命判断、参数化、死亡测试等等。

调用流程和框架

gtest的运行可大体分为两部分:注册和运行。首先来说注册。

注册流程:

2808 TestInfo* MakeAndRegisterTestInfo(

2809     const char* test_case_name,

2810     const char* name,

2811     const char* type_param,

2812     const char* value_param,

2813     TypeId fixture_class_id,

2814     SetUpTestCaseFunc set_up_tc,

2815     TearDownTestCaseFunc tear_down_tc,

2816     TestFactoryBase* factory) {

2817   TestInfo* const test_info =

2818       new TestInfo(test_case_name, name, type_param, value_param,

2819                    fixture_class_id, factory);

2820   GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

2821   return test_info;

2822 }

676   void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,

677                    Test::TearDownTestCaseFunc tear_down_tc,

678                    TestInfo* test_info) {

679     // In order to support thread-safe death tests, we need to

680     // remember the original working directory when the test program

681     // was first invoked.  We cannot do this in RUN_ALL_TESTS(), as

682     // the user may have changed the current directory before calling

683     // RUN_ALL_TESTS().  Therefore we capture the current directory in

684     // AddTestInfo(), which is called to register a TEST or TEST_F

685     // before main() is reached.

686     if (original_working_dir_.IsEmpty()) {

687       original_working_dir_.Set(FilePath::GetCurrentDir());

688       GTEST_CHECK_(!original_working_dir_.IsEmpty())

689           << "Failed to get the current working directory.";

690     }

691       

692     GetTestCase(test_info->test_case_name(),

693                 test_info->type_param(),

694                 set_up_tc,

695                 tear_down_tc)->AddTestInfo(test_info);

696   }

有这样一个功能,gtest就可以在运行main函数之前利用静态变量赋值的方法来调用MakeAndRegisterTestInfo函数。

在MakeAndRegisterTestInfo函数中,首先创建新的test_info,调用AddTestInfo,AddTestInfo函数会调用GetTestCase,从test_cases_数组中查找想要获取的test_case,如果没有就新建一个,添加到test_cases_数组中,然后调用对应的TestCase的addTestInfo函数来添加test_info到test_info_list_.

这里需要解释一下的是,在gtest的框架中TestInfo类就是我们所说的test_case,TestCase类对应我们所说的test_suit,gtest框架有2级,一级是全局TestCase数组test_cases_,包含所有我们注册的test_suit,二级是每个TestCase类中的TestInfo数组test_info_list_,包含所有我们注册的test_case.

注册就说完了,总结一下,gtest是利用静态变量动态初始化,调用函数MakeAndRegisterTestInfo,来实现了对test_case注册的。

(注意点:

全局变量的初始化顺序是不确定的。编译器不能定义不同编译单元全局变量的初始化顺序。

在同一个cpp文件里,编译器会保证按照他们出现的顺序初始化。但是如果两个全局变量在不同的编译单元,编译器不能控制他们的构造顺序,如果项目对其顺序有要求,则我们需要想办法确定他们的构造顺序。)

运行框架

TEST宏总结

首先声明一个以test_case_name和test_name拼接成的类,所以需要保证test_case_name和test_name在代码中唯一,避免命名冲突。

拼接的类中有一个静态成员变量test_info_,该成员变量的唯一作用是利用类静态变量在main之前初始化,对测试用例的信息进行注册。这是GTEST的最精华部分

通过MakeAndRegisterTestInfo函数对test_info_进行赋初值的过程,对测试用例的信息进行注册。因为UnitTest和UnitTestImpl均实现为单例,实现了注册测试用例信息到GTest中。

通过宏定义,实现了TestBody()的逻辑,即我们需要的测试代码,这部分会在运行测试用例时被调用。

TEST宏的主要目的在于提取注册测试用例信息,为后续运行测试用例做准备。

GTest自动化测试

对于一个测试自动化框架,主要通过三个方面对其进行评估,分别是数据驱动,测试结果和异常处理。

数据驱动能力

GTest中有三种方法:分别是测试固件类(Test Fixture)、值参数化测试(Value-Parameterized Test)和类型参数化测试(Type-Parameterized Test)

测试固件 (Test Fixture)

在TEST()的使用中,我们接触了一个测试用例包含多个测试实例的组织方式。多个测试实例可能需要进行相似的数据配置和初始化操作,为此,Gtest提供了测试固件(Test fixture)帮助我们进行数据管理。测试固件类可以使得不同的测试共享相同的测试数据配置。

所有的fixture的定义都必须继承::testing::test类,该类其实就是一个接口,定义了SetUp和TearDown接口函数,对负责测试用例执行前后环境的生成和撤销。

值参数化测试

值参数化测试和Fixture类的作用类似,也是使得不同的测试共享相同的测试数据配置,但是略有不同。

类型参数化测试

类型参数化测试,和值参数化测试相似,不同的是用类型来作为参数,故需要使用模板技术来实现。其优点是可以对操作类似或者相同的类型,使用一个Fixture模板来实现所有类的测试用例。

上面的三种机制可以使得在编写测试程序的时候减少代码量。但是,GTest目前仍然没有提供一套内置的完整的数据驱动机制,故仍存在测试用例和测试数据维护管理麻烦等问题。

gtest解析

TEST(test_case_name, test_name)

可以看到TEST宏经过预处理器处理后展开为:

定义了一个继承自::testing::test类的新类test_case_name_test_name_Test,该类的名字为TEST宏两个形参的拼接而成.

TEST宏中的测试代码被展开并定义为生成类的成员函数TestBody的函数体。

生成类的静态数据成员test_info_被初始化为函数MakeAndRegisterTestInfo的返回值。具体意义后面再介绍。

声明了:private类型 的copy函数和assign函数,因此不允许copy和assign操作

MakeAndRegisterTestInfo函数

从上面来看MakeAndRegisterTestInfo函数是一个比较关键的函数了,从字面意思上看就是生成并注册该案例的信息,在头文件gtest.cc中可以找到关于它的定义,它是一个testing命名空间中嵌套命名空间internal中的非成员函数:

TestInfo* MakeAndRegisterTestInfo(

    const char* test_case_name, const char* name,

    const char* type_param,

    const char* value_param,

    TypeId fixture_class_id,

    SetUpTestCaseFunc set_up_tc,

    TearDownTestCaseFunc tear_down_tc,

    TestFactoryBase* factory) {

  TestInfo* const test_info =

      new TestInfo(test_case_name, name, type_param, value_param,

                   fixture_class_id, factory);

  GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

  return test_info;

}

其中形参的意义如下:

test_case_name:测试套名称,即TEST宏中的第一个形参。

name:测试案例名称。

type_param:测试套的附加信息。默认为无

value_param:测试案例的附加信息。默认为无

fixture_class_id:test fixture类的id

set_up_tc :函数指针,指向函数SetUpTestCaseFunc

tear_down_tc:函数指针,指向函数TearDownTestCaseFunc

factory:指向工厂对象的指针,该工厂对象创建上面TEST宏生成的测试类的对象

我们看到在MakeAndRegisterTestInfo函数体中定义了一个TestInfo对象,该对象包含了一个TEST宏中标识的测试案例的测试套名称、测试案例名称、测试套附加信息、测试案例附加信息、创建测试案例类对象的工厂对象的指针这些信息。

所谓的工厂对象,可以在gtest-internal.h中找到它的定义

template <class TestClass>

class TestFactoryImpl : public TestFactoryBase {

 public:

  virtual Test* CreateTest() { return new TestClass; }

};

TestFactoryImpl类是一个模板类,它的作用就是单纯的创建对应于模板形参类型的测试案例对象。因为模板的存在也大大简化了代码,否则可能就要写无数个TestFactoryImpl类

GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

乍一看似乎是对test_info对象的一些熟悉信息进行设置。究竟是怎么样呢?源码面前,了无秘密,我们还是得去找到它的源码,在gtest-internal-inl中可以找到它的定义

inline UnitTestImpl* GetUnitTestImpl() {

  return UnitTest::GetInstance()->impl();

}

它的实现也是非常简单,关键还是在UnitTest类的成员函数GetInstance和返回类型的成员函数impl,继续追踪下去

class GTEST_API_ UnitTest {

public:

// Gets the singleton UnitTest object.  The first time this method

// is called, a UnitTest object is constructed and returned.

// Consecutive calls will return the same object.

  static UnitTest* GetInstance();


  internal::UnitTestImpl* impl() { return impl_; }

  const internal::UnitTestImpl* impl() const { return impl_; }


private:

  mutable internal::Mutex mutex_;

  internal::UnitTestImpl* impl_;

}


UnitTest * UnitTest::GetInstance() {

#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)

   static UnitTest* const instance = new UnitTest;

   return instance;

#else

   static UnitTest instance;

   return &instance;

}

根据代码和注释可知GetInstance是Unitest类的成员函数,它仅仅是生成一个静态的UniTest对象然后返回。实际上这么做是为了实现UniTest类的单例(Singleton)实例。而impl只是单纯的返回UniTest的UnitTestImpl类型的指针数据成员impl_。

再联系之前的代码,通过UnitTestImpl类的AddTestInfo设置Test_Info类对象的信息。其实绕了一圈,最终就是通过AddTestInfo设置Test_info类对象的信息,自然地,我们需要知道AddTestInfo的实现啦:

void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,

                   Test::TearDownTestCaseFunc tear_down_tc,

                   TestInfo* test_info) {

    GetTestCase(test_info->test_case_name(),

                test_info->type_param(),

                set_up_tc,

                tear_down_tc)->AddTestInfo(test_info);

}

而AddTestInfo是通过GetTestCase函数实现的

TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,

                                    const char* type_param,

                                    Test::SetUpTestCaseFunc set_up_tc,

                                    Test::TearDownTestCaseFunc tear_down_tc) {

  // Can we find a TestCase with the given name?

  const std::vector<TestCase*>::const_iterator test_case =

      std::find_if(test_cases_.begin(), test_cases_.end(),

                   TestCaseNameIs(test_case_name));


  if (test_case != test_cases_.end())

    return *test_case;


  // No.  Let's create one.

  TestCase* const new_test_case =

      new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);


  // Is this a death test case?

  if (internal::UnitTestOptions::MatchesFilter(String(test_case_name),

                                               kDeathTestCaseFilter)) {

    // Yes.  Inserts the test case after the last death test case

    // defined so far.  This only works when the test cases haven't

    // been shuffled.  Otherwise we may end up running a death test

    // after a non-death test.

    ++last_death_test_case_;

    test_cases_.insert(test_cases_.begin() + last_death_test_case_,

                       new_test_case);

  } else {

    // No.  Appends to the end of the list.

    test_cases_.push_back(new_test_case);

  }


  test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));

  return new_test_case;

}

从上面代码可以看出其实并不是一开始猜测的设置Test_Info对象的信息,而是判断包含Test_info对象中的测试套名称、测试案例名称等信息的TestCase对象的指针是否在一个vector向量中,若存在就返回这个指针;若不存在就把创建一个包含这些信息的TestCase对象的指针加入到vector向量中,并返回这个指针。

至于vector向量test_cases_,它是UnitTestImpl中的私有数据成员,在这个向量中存放了整个测试项目中所有包含测试套、测试案例等信息的TestCase对象的指针。

紧接着我们看到从GetTestCase返回的TestCase指针调用TestCase类中的成员函数AddTestInfo,在gtest.cc中可以找到它的定义如下:

void TestCase::AddTestInfo(TestInfo * test_info) {

  test_info_list_.push_back(test_info);

  test_indices_.push_back(static_cast<int>(test_indices_.size()));

}

调用这个函数的目的是在于将Test_info对象添加到test_info_list_中,而test_info_list_是类TestCase中的私有数据成员,它也是一个vector向量。原型为

std::vector<TestInfo*> test_info_list_;

该向量保存着整个项目中所有包含测试案例对象各种信息的Test_Info对象的指针。

而test_indices_也是类TestCase中的私有数据成员,保存着test_info_list中每个元素的索引号。它仍然是一个vector向量,原型为

std::vector<int> test_indices_;

gtest系列之TEST宏

看一下TEST宏的具体定义实现:

#if !GTEST_DONT_DEFINE_TEST

# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)

#endif

#define GTEST_TEST(test_case_name, test_name)\

  GTEST_TEST_(test_case_name, test_name, \

              ::testing::Test, ::testing::internal::GetTestTypeId())

#define TEST_F(test_fixture, test_name)\

  GTEST_TEST_(test_fixture, test_name, test_fixture, \

              ::testing::internal::GetTypeId<test_fixture>())

可以看到,TEST宏和事件机制对应的TEST_F宏都是调用了GTEST_TEST_宏,我们再追踪这个宏的定义

#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\

class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\

 public:\

  GTEST_TEST_CLASS_NAME_(test_case_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_case_name, test_name));\

};\

\

::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\

  ::test_info_ =\

    ::testing::internal::MakeAndRegisterTestInfo(\

        #test_case_name, #test_name, NULL, NULL, \

        (parent_id), \

        parent_class::SetUpTestCase, \

        parent_class::TearDownTestCase, \

        new ::testing::internal::TestFactoryImpl<\

            GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\

void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()

可以看到在预处理展开中的代码了,其中GTEST_TEST_CLASS_NAME_,从字面意思可以知道这宏就是获得类的名字

#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \

  test_case_name##_##test_name##_Test

果不其然,宏GTEST_TEST_CLASS_NAME的功能就是把两个参数拼接为一个参数。

RUN_ALL_TEST宏

我们的测试程序就是从main函数的RUN_ALL_TESTS的调用开始的,在gtest.h中可以找到该宏的定义

#define RUN_ALL_TESTS()\

  (::testing::UnitTest::GetInstance()->Run())

RUN_ALL_TESTS就是简单的调用UnitTest的成员函数GetInstance,我们知道GetInstance就是返回一个单例(Singleton)UnitTest对象,该对象调用成员函数Run

int UnitTest::Run() {

  impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));


  return internal::HandleExceptionsInMethodIfSupported(

      impl(),

      &internal::UnitTestImpl::RunAllTests,

     "auxiliary test code (environments or event listeners)") ? 0 : 1;

}

Run()函数也是简单的调用HandleExceptionInMethodIfSupported函数,追踪它的实现

template <class T, typename Result>

Result HandleExceptionsInMethodIfSupported(

    T* object, Result (T::*method)(), const char* location) {


    if (internal::GetUnitTestImpl()->catch_exceptions()) {

     ......  //异常处理省略

     } else {

     return (object->*method)();

   }

 }

HandleExceptionsInMethodIfSupported是一个模板函数,他的模板形参具现化为调用它的UnitTestImpl和int,也就是T = UnitTestImpl, Result = int。在函数体里调用UnitTestImpl类的成员函数RunAllTests

bool UnitTestImpl::RunAllTests() {

    ......

    const TimeInMillis start = GetTimeInMillis();  //开始计时

    if (has_tests_to_run && GTEST_FLAG(shuffle)) {

       random()->Reseed(random_seed_);

       ShuffleTests();

     }

     repeater->OnTestIterationStart(*parent_, i);


     if (has_tests_to_run) {

       //初始化全局的SetUp事件

       repeater->OnEnvironmentsSetUpStart(*parent_);

       //顺序遍历注册全局SetUp事件

       ForEach(environments_, SetUpEnvironment);

       //初始化全局TearDown事件

       repeater->OnEnvironmentsSetUpEnd(*parent_);

       //

       // set-up.

       if (!Test::HasFatalFailure()) {

         for (int test_index = 0; test_index < total_test_case_count();

              test_index++) {

           GetMutableTestCase(test_index)->Run(); //TestCase::Run

         }

       }

      // 反向遍历取消所有全局事件.

      repeater->OnEnvironmentsTearDownStart(*parent_);

     std::for_each(environments_.rbegin(), environments_.rend(),

                    TearDownEnvironment);

      repeater->OnEnvironmentsTearDownEnd(*parent_);

    }

    elapsed_time_ = GetTimeInMillis() - start; //停止计时

    ......

}

如上面代码所示,UnitTestImpl::RunAllTests主要进行全局事件的初始化,以及变量注册。而真正的执行部分在于调用GetMutableTestCase

TestCase* UnitTest::GetMutableTestCase(int i) {

  return impl()->GetMutableTestCase(i); //impl返回UnitTestImpl类型指针

}


TestCase* UnitTestImpl:: GetMutableTestCase(int i) {

    const int index = GetElementOr(test_case_indices_, i, -1);

    return index < 0 ? NULL : test_cases_[index];

}

经过两次调用返回vector向量test_cases_中的元素,它的元素类型为TestCase类型。然后调用TestCase::Run

void TestCase::Run() {

  ......  //省略

  const internal::TimeInMillis start = internal::GetTimeInMillis();

  for (int i = 0; i < total_test_count(); i++) {

    GetMutableTestInfo(i)->Run(); //调用TestCase::GetMutableTestInfo

  }                                     //以及Test_Info::Run

  ...... //省略

}


TestInfo* TestCase::GetMutableTestInfo(int i) {

  const int index = GetElementOr(test_indices_, i, -1);

  return index < 0 ? NULL : test_info_list_[index];

}

看到又转向调用TestCase::GetMutableTestInfo,返回向量test_info_list_的元素。而它的元素类型为Test_info。进而又转向了Test_info::Run

void TestInfo::Run() {

  ......  //省略

  Test* const test = internal::HandleExceptionsInMethodIfSupported(

      factory_, &internal::TestFactoryBase::CreateTest,

      "the test fixture's constructor");

  ......  //省略

    test->Run();  // Test::Run

  ......   //省略

  }

在TestInfo::Run中调用了HandleExceptionsInMethodIfSupported,通过上文中的分析可以得知该函数在这个地方最终的作用是调用internal::TestFactoryBase::CreateTest将factor_所指的工厂对象创建的测试案例对象的地址赋给Test类型的指针test。所以最后调用了Test::Run。

void Test::Run() {

  if (!HasSameFixtureClass()) return;


  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();

  impl->os_stack_trace_getter()->UponLeavingGTest();

  internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");

  // We will run the test only if SetUp() was successful.

  if (!HasFatalFailure()) {

    impl->os_stack_trace_getter()->UponLeavingGTest();

    internal::HandleExceptionsInMethodIfSupported(

        this, &Test::TestBody, "the test body");

  }


  // However, we want to clean up as much as possible.  Hence we will

  // always call TearDown(), even if SetUp() or the test body has

  // failed.

  impl->os_stack_trace_getter()->UponLeavingGTest();

  internal::HandleExceptionsInMethodIfSupported(

      this, &Test::TearDown, "TearDown()");

}

在Test::Run函数体中我们看到通过HandleExceptionsInMethodIfSupported调用了TestBody,先来看看Test中TestBody的原型声明

virtual void TestBody() = 0;

TestBody被声明为纯虚函数。一切都明朗了,在上文中通过test调用Test::Run,进而通过test::调用TestBody,而test实际上是指向继承自Test类的案例类对象,进而发生了多态,调用的是Test_foo_test_normal_Test::TestBody,也就是我们最初在TEST或者TEST_F宏中所写的测试代码。

如此遍历,就是顺序执行测试demo程序中所写的每一个TEST宏的函数体啦。

总结

经过对预处理得到的TEST宏进行逆向跟踪,到正向跟踪RUN_ALL_TESTS宏,了解了gtest的整个运行过程,里面涉及到一下GOF涉及模式的运用,比如工厂函数、Singleton、Impl等。

另外本文没有提到的地方如断言宏、输出log日志等,因为比较简单就略过了。断言宏和输出log就是在每次遍历调用TestBody的时候进行相应的判断和输出打印。

下图是一个简单的TEST宏展开后的流程图

最后再简单将gtest的运行过程简述一遍:

整个测试项目只有一个UnitTest对象,因而整个项目也只有一个UnitTestImpl对象

每一个TEST宏生成一个测试案例类,继承自Test类

对于每一个测试案例类,由一个工厂类对象创建该类对象

由该测试案例类对象创建一个Test_Info类对象

由Test_Info类对象创建一个Test_case对象

创建Test_case对象的指针,并将其插入到UnitTestImpl对象的数据成员vector向量的末尾位置

对每一个TEST宏进行2-6步骤,那么对于唯一一个UnitTestImpl对象来说,它的数据成员vector向量中的元素按顺序依次指向每一个包含测试案例对象信息的TestCase对象。

执行RUN_ALL_TESTS宏,开始执行用例。从头往后依次遍历UnitTestImpl对象中vector向量中的元素,对于其中的每一个元素指针,经过一系列间接的方式最终调用其所对应的测试案列对象的TestBody成员函数,即测试用例代码。

gtest提供了多种事件机制,非常方便我们在案例之前或之后做一些操作。

总结一下gtest的事件一共有3种:

全局的,所有案例执行前后

TestSuite级别的,在某一批案例中第一个案例前,最后一个案例执行后

TestCase级别的,每个TestCase前后

测试程序:一个测试程序只有一个main函数,也可以说是一个可执行程序是一个测试程序。该级别的事件机制会在程序的开始和结束执行。

测试套件:代表一个测试用例的集合体,该级别的事件机制会在整体的测试案例开始和结束执行。

测试用例:该级别的的事件机制会在每个测试用例开始和结束都执行。

全局的事件机制(针对整个测试程序)

局部的事件机制(针对一个个测试套件)

个体的事件机制(针对一个个测试用例)

一、全局的事件机制

实现全局的事件机制,需要创建一个自己的类,然后继承testing::Environment类,然后分别实现成员函数SetUp()和TearDown(),同时在main函数内执行调用,即“testing::AddGlobalTestEnvironment(new MyEnvironment);”,通过调用函数我们可以添加多个全局的事件机制。

SetUp()函数是在所有测试开始前执行。TearDown()函数是在所有测试结束后执行。

示例:

二、局部的事件机制

测试套件的事件机制我们同样需要去创建一个类,继承testing::Test,实现两个静态函数SetUpTestCase()和TearDownTestCase(),测试套件的事件机制不需要像全局事件机制一样在main注册,而是需要将我们平时使用的TEST宏改为TEST_F宏。

SetUpTestCase()函数是在测试套件第一个测试用例开始前执行。TearDownTestCase()函数是在测试套件最后一个测试用例结束后执行。

需要注意TEST_F的第一个参数使我们创建的类名,也就是当前测试套件的名称。

示例:

三、个体事件机制

测试用例的事件机制的创建和测试套件的基本一样,不同地方在于测试用例实现的两个函数分别是SetUp()和TearDown(),这两个函数不是静态函数了。

SetUp()函数是在一个测试用例的开始前执行。TearDown()函数是在一个测试用例的结束后执行。

四、总结

gtest的三种事件机制总的来说还是简单的,而且也比较灵活,通过上面的例子也能看出我们可以在事件机制中实现一些资源共享,使我们的测试更加灵活。

gtest系列之断言

gtest中断言的宏可以分为两类:一类是ASSERT宏,另一类是EXPECT宏。

ASSERT_系列:如果当前点检测失败则退出当前函数

EXCEPT_系列:如果当前点检测失败则继续往下执行

如果你对自动输出的错误信息不满意的话,也是可以通过operator<<能够在失败的时候打印日志,将一些自定义的信息输出。

ASSERT_系列:

bool值检查

1、 ASSERT_TRUE(参数),期待结果是true

2、ASSERT_FALSE(参数),期待结果是false

数值型数据检查

3、ASSERT_EQ(参数1,参数2),传入的是需要比较的两个数 equal

4、ASSERT_NE(参数1,参数2),not equal,不等于才返回true

5、ASSERT_LT(参数1,参数2),less than,小于才返回true

6、ASSERT_GT(参数1,参数2),greater than,大于才返回true

7、ASSERT_LE(参数1,参数2),less equal,小于等于才返回true

8、ASSERT_GE(参数1,参数2),greater equal,大于等于才返回true

字符串检查

9、ASSERT_STREQ(expected_str, actual_str),两个C风格的字符串相等才正确返回

10、ASSERT_STRNE(str1, str2),两个C风格的字符串不相等时才正确返回

11、ASSERT_STRCASEEQ(expected_str, actual_str)

12、ASSERT_STRCASENE(str1, str2)

13、EXPECT_系列,也是具有类似的宏结构的

gtest系列之死亡测试

这里的”死亡”指的是程序的奔溃。通常在测试的过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序奔溃,这个时候我们就要检查程序是否按照预期的方式挂掉,这也就是所谓的”死亡测试”。

死亡测试所用到的宏:

1、ASSERT_DEATH(参数1,参数2),程序挂了并且错误信息和参数2匹配,此时认为测试通过。如果参数2为空字符串,则只需要看程序挂没挂即可。

2、ASSERT_EXIT(参数1,参数2,参数3),语句停止并且错误信息和被提前给的信息匹配。

玩转Google单元测试框架gtest系列之六:运行参数

一、前言

使用gtest编写的测试案例通常本身就是一个可执行文件,因此运行起来非常方便。同时,gtest也为我们提供了一系列的运行参数(环境变量、命令行参数或代码里指定),使得我们可以对案例的执行进行一些有效的控制

二、基本介绍

前面提到,对于运行参数,gtest提供了三种设置的途径:

系统环境变量

命令行参数

代码中指定FLAG

因为提供了三种途径,就会有优先级的问题,有一个原则是,最后设置的那个会生效。不过总结一下,通常情况下,比较理想的优先级为:

命令行参数 > 代码中指定FLAG > 系统环境变量

为什么我们编写的测试案例能够处理这些命令行参数呢?是因为我们在main函数中,将命令行参数交给了gtest,由gtest来搞定命令行参数的问题。

参考:

1  Gtest三种事件机制

解析gtest框架运行机制  

3   gtest实现框架简单分析

gtest的介绍和使用

GTest源码剖析(二)——TEST宏

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容