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来搞定命令行参数的问题。
参考: