文章目录
- 1. GTest 是什么
- 2. GTest 使用
- 2.1 TEST 宏
- 2.2 断言
- 2.3 事件机制
1. GTest 是什么
GTest 是一个跨平台的C++单元测试框架,由 google 公司发布。gtest 是为了在不同平台上为编写 C++单元测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化等等
2. GTest 使用
2.1 TEST 宏
TEST(test_case_name,test_name)TEST_F(test_fixture,test_name)- TEST:主要用来创建一个简单测试, 它定义了一个测试函数, 在这个函数中可以使用任何 C++代码并且使用框架提供的断言进行检查
- TEST_F:主要用来进行多样测试,适用于多个测试场景如果需要相同的数据配置的情况, 即相同的数据测不同的行为
2.2 断言
GTest 中的断言的宏可以分为两类:
- ASSERT_系列:如果当前点检测失败则退出当前函数
- EXPECT_系列:如果当前点检测失败则继续往下执行
下面是经常使用的断言介绍
// bool 值检查ASSERT_TRUE(参数),期待结果是trueASSERT_FALSE(参数),期待结果是false//数值型数据检查ASSERT_EQ(参数1,参数2),传入的是需要比较的两个数 equalASSERT_NE(参数1,参数2),notequal,不等于才返回trueASSERT_LT(参数1,参数2),less than,小于才返回trueASSERT_GT(参数1,参数2),greater than,大于才返回trueASSERT_LE(参数1,参数2),less equal,小于等于才返回trueASSERT_GE(参数1,参数2),greater equal,大于等于才返回true注:如果你对自动输出的错误信息不满意的话,也可以通过 operator<< 在失败的时候打印自定义日志
下面我们做个测试, 实现一个求绝对值的函数, 并且使用 TEST 宏及断言进行测试:
#include<iostream>#include<gtest/gtest.h>/* 断言宏的使用 ASSERT_ 断言失败则退出 EXPECT_ 断言失败继续运行 注意: 断言宏,必须在单元测试宏函数中使用 */intabs(intx){returnx>0?x:-x;}TEST(abs_test,test1){ASSERT_TRUE(abs(1)==1)<<"abs(1)=1";ASSERT_TRUE(abs(-1)==1);ASSERT_FALSE(abs(-2)==-2);ASSERT_EQ(abs(1),abs(-1));ASSERT_NE(abs(-1),0);ASSERT_LT(abs(-1),2);ASSERT_GT(abs(-1),0);ASSERT_LE(abs(-1),2);ASSERT_GE(abs(-1),0);}intmain(intargc,char*argv[]){// 将命令行参数传递给 gtesttesting::InitGoogleTest(&argc,argv);// 运行所有测试案例returnRUN_ALL_TESTS();}编译运行:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$makeg++ -o assert assert.cc -std=c++11 -lgtest ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./assert[==========]Running1testfrom1testsuite.[----------]Globaltestenvironment set-up.[----------]1testfrom abs_test[RUN]abs_test.test1[OK]abs_test.test1(1ms)[----------]1testfrom abs_test(1ms total)[----------]Globaltestenvironment tear-down[==========]1testfrom1testsuite ran.(1ms total)[PASSED]1test.将第一条语句改成 ASSERT_FALSE 进行错误测试:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./assert[==========]Running1testfrom1testsuite.[----------]Globaltestenvironment set-up.[----------]1testfrom abs_test[RUN]abs_test.test1 assert.cc:18: Failure Value of: abs(1)==1Actual:trueExpected:falseabs(1)=1[FAILED]abs_test.test1(1ms)[----------]1testfrom abs_test(1ms total)[----------]Globaltestenvironment tear-down[==========]1testfrom1testsuite ran.(1ms total)[PASSED]0tests.[FAILED]1test, listed below:[FAILED]abs_test.test11FAILED TEST注意:ASSERT_FALSE(abs(1) == 1)失败会立即终止当前测试函数(abs_test.test1)。后续所有断言(第19-26行)都不会执行。但如果使用EXPECT_FALSE(abs(1) == 1)失败,会继续执行当前测试函数。框架会记录失败,但继续检查后面的ASSERT_TRUE、ASSERT_EQ等断言。
2.3 事件机制
GTest 中的事件机制是指在测试前和测试后提供给用户自行添加操作的机制,而且该机制也可以让同一测试套件下的测试用例共享数据。GTest 框架中事件的结构层次:
- 测试程序:一个测试程序只有一个 main 函数,也可以说是一个可执行程序是一个测试程序。该级别的事件机制是在程序的开始和结束执行
- 测试套件:代表一个测试用例的集合体,该级别的事件机制是在整体的测试案例开始和结束执行
- 测试用例:该级别的事件机制是在每个测试用例开始和结束都执行
事件机制的最大好处就是能够为我们各个测试用例提前准备好测试环境,并在测试完毕后用于销毁环境,这样有个好处就是如果我们有一端代码需要进行多种不同方法的测试,则可以通过测试机制在每个测试用例进行之前初始化测试环境和数据,并在测试完毕后清理测试造成的影响。
GTest 提供了三种常见的的事件:
- 全局事件:针对整个测试程序。实现全局的事件机制,需要创建一个自己的类,然后继承 testing::Environment 类,然后分别实现成员函数 SetUp 和 TearDown,同时在 main 函数内进行调用 testing::AddGlobalTestEnvironment(new MyEnvironment);函数添加全局的事件机制
#include<iostream>#include<gtest/gtest.h>#include<unordered_map>//全局事件:针对整个测试程序,提供全局事件机制,能够在测试之前配置测试环境数据,测试完毕后清理数据//先定义环境类,通过继承 testing::Environment 的派生类来完成//重写的虚函数接口 SetUp 会在测试之前被调用; TearDown 会在测试完毕后调用.classMyEnvironment:publictesting::Environment{public:virtualvoidSetUp()override{std::cout<<"单元测试执行前的环境初始化!!\n";}virtualvoidTearDown()override{std::cout<<"单元测试执行完毕后的环境清理!!\n";}};TEST(MyEnvironment,test1){std::cout<<"这是单元测试1\n";}TEST(MyEnvironment,test2){std::cout<<"这是单元测试2\n";}intmain(intargc,char*argv[]){testing::InitGoogleTest(&argc,argv);testing::AddGlobalTestEnvironment(newMyEnvironment);returnRUN_ALL_TESTS();}运行结果:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$makeg++ -o global global.cc -std=c++11 -lgtest ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./global[==========]Running2tests from1testsuite.[----------]Globaltestenvironment set-up. 单元测试执行前的环境初始化!![----------]2tests from MyEnvironment[RUN]MyEnvironment.test1 这是单元测试1[OK]MyEnvironment.test1(0ms)[RUN]MyEnvironment.test2 这是单元测试2[OK]MyEnvironment.test2(0ms)[----------]2tests from MyEnvironment(0ms total)[----------]Globaltestenvironment tear-down 单元测试执行完毕后的环境清理!![==========]2tests from1testsuite ran.(0ms total)[PASSED]2tests.我们可以再来一个全局事件:
#include<iostream>#include<gtest/gtest.h>#include<unordered_map>// 全局事件:针对整个测试程序,提供全局事件机制,能够在测试之前配置测试环境数据,测试完毕后清理数据// 先定义环境类,通过继承 testing::Environment 的派生类来完成// 重写的虚函数接口 SetUp 会在测试之前被调用; TearDown 会在测试完毕后调用.classMyEnvironment:publictesting::Environment{public:virtualvoidSetUp()override{std::cout<<"单元测试执行前的环境初始化!!\n";}virtualvoidTearDown()override{std::cout<<"单元测试执行完毕后的环境清理!!\n";}};TEST(MyEnvironment,test1){std::cout<<"这是单元测试1\n";}TEST(MyEnvironment,test2){std::cout<<"这是单元测试2\n";}std::unordered_map<std::string,std::string>mymap;classMyMapTest:publictesting::Environment{public:virtualvoidSetUp()override{std::cout<<"单元测试执行前的环境初始化!!\n";mymap.insert(std::make_pair("hello","你好"));mymap.insert(std::make_pair("bye","再见"));}virtualvoidTearDown()override{std::cout<<"单元测试执行完毕后的环境清理!!\n";mymap.clear();}};TEST(MyMapTest,test1){ASSERT_EQ(mymap.size(),2);mymap.erase("hello");}TEST(MyMapTest,test2){ASSERT_EQ(mymap.size(),2);}intmain(intargc,char*argv[]){testing::InitGoogleTest(&argc,argv);testing::AddGlobalTestEnvironment(newMyEnvironment);testing::AddGlobalTestEnvironment(newMyMapTest);returnRUN_ALL_TESTS();}编译运行:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$makeg++ -o global global.cc -std=c++11 -lgtest ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./global[==========]Running4tests from2testsuites.[----------]Globaltestenvironment set-up. 单元测试执行前的环境初始化!! 单元测试执行前的环境初始化!![----------]2tests from MyEnvironment[RUN]MyEnvironment.test1 这是单元测试1[OK]MyEnvironment.test1(0ms)[RUN]MyEnvironment.test2 这是单元测试2[OK]MyEnvironment.test2(0ms)[----------]2tests from MyEnvironment(0ms total)[----------]2tests from MyMapTest[RUN]MyMapTest.test1[OK]MyMapTest.test1(0ms)[RUN]MyMapTest.test2 global.cc:56: Failure Expected equality of these values: mymap.size()Which is:12[FAILED]MyMapTest.test2(1ms)[----------]2tests from MyMapTest(1ms total)[----------]Globaltestenvironment tear-down 单元测试执行完毕后的环境清理!! 单元测试执行完毕后的环境清理!![==========]4tests from2testsuites ran.(1ms total)[PASSED]3tests.[FAILED]1test, listed below:[FAILED]MyMapTest.test21FAILED TEST这两个全局事件都是在单元测试之前初始化和单元测试之后清理
- TestSuite 事件:针对一个个测试套件。测试套件的事件机制我们同样需要去创建一个类,继承自 testing::Test,实现两个静态函数 SetUpTestCase 和 TearDownTestCase,测试套件的事件机制不需要像全局事件机制一样在 main 注册,而是需要将我们平时使用的 TEST 宏改为 TEST_F 宏。
- SetUpTestCase() 函数是在测试套件第一个测试用例开始前执行
- TearDownTestCase() 函数是在测试套件最后一个测试用例结束后执行
- 需要注意 TEST_F 的第一个参数是我们创建的类名,也就是当前测试套件的名称,这样在 TEST_F 宏的测试套件中就可以访问类中的成员了。
#include<iostream>#include<gtest/gtest.h>#include<unordered_map>// TestSuite:测试套件/集合进行单元测试,即,将多个相关测试归入一组的方式进行测试, 为这组测试用例进行环境配置和清理// 概念: 对一个功能的验证往往需要很多测试用例,测试套件就是针对一组相关测试用例进行环境配置的事件机制// 用法: 先定义环境类,继承于 testing::Test 基类, 重写两个静态函数SetUpTestCase / TearDownTestCase 进行环境的配置和清理classHashTestEnv1:publictesting::Test{public:staticvoidSetUpTestCase(){std::cout<<"环境 1 第一个 TEST 之前调用\n";}staticvoidTearDownTestCase(){std::cout<<"环境 1 最后一个 TEST 之后调用\n";}public:std::unordered_map<std::string,std::string>dict;};// 注意,测试套件使用的不是 TEST 了,而是 TEST_F, 而第一个参数名称就是测试套件环境类名称// main 函数中不需要再注册环境了,而是在 TEST_F 中可以直接访问类的成员变量和成员函数TEST_F(HashTestEnv1,insert_test){std::cout<<"环境 1,中间 insert 测试\n";dict.insert(std::make_pair("Hello","你好"));dict.insert(std::make_pair("hello","你好"));dict.insert(std::make_pair("雷吼","你好"));autoit=dict.find("hello");ASSERT_NE(it,dict.end());}TEST_F(HashTestEnv1,sizeof){std::cout<<"环境 1,中间 size 测试\n";ASSERT_GT(dict.size(),0);}intmain(intargc,char*argv[]){testing::InitGoogleTest(&argc,argv);returnRUN_ALL_TESTS();}运行结果:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$makeg++ -o suite suite.cc -std=c++11 -lgtest ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./suite[==========]Running2tests from1testsuite.[----------]Globaltestenvironment set-up.[----------]2tests from HashTestEnv1 环境1第一个 TEST 之前调用[RUN]HashTestEnv1.insert_test 环境1,中间 insert 测试[OK]HashTestEnv1.insert_test(0ms)[RUN]HashTestEnv1.sizeof 环境1,中间 size 测试 suite.cc:36: Failure Expected:(dict.size())>(0), actual:0vs0[FAILED]HashTestEnv1.sizeof(0ms)环境1最后一个 TEST 之后调用[----------]2tests from HashTestEnv1(0ms total)[----------]Globaltestenvironment tear-down[==========]2tests from1testsuite ran.(0ms total)[PASSED]1test.[FAILED]1test, listed below:[FAILED]HashTestEnv1.sizeof1FAILED TEST能够看到在上例中,有一个好处,就是将数据与测试结合到同一个测试环境类中了,这样与外界的耦合度更低,代码也更清晰。
但是同样的,我们发现在两个测试用例中第二个测试用例失败了,这是为什么呢?这就涉及到了 TestCase 事件的机制。
- TestCase 事件: 针对一个个测试用例。测试用例的事件机制的创建和测试套件的基本一样,不同地方在于测试用例实现的两个函数分别是 SetUp 和 TearDown, 这两个函数也不是静态函数
- SetUp()函数是在一个测试用例的开始前执行
- TearDown()函数是在一个测试用例的结束后执行
也就是说,在 TestSuite/TestCase 事件中,每个测试用例,虽然它们同用同一个事件环境类,可以访问其中的资源,但是本质上每个测试用例的环境都是独立的,这样我们就不用担心不同的测试用例之间会有数据上的影响了,保证所有的测试用例都使用相同的测试环境进行测试。
#include<iostream>#include<gtest/gtest.h>#include<unordered_map>// TestCase:测试用例的单元测试,即针对每一个测试用例都使用独立的测试环境数据进行测试// 概念:它是针对测试用例进行环境配置的一种事件机制// 用法:先定义环境类,继承于 testing::Test 基类, 在环境类内重写SetUp/TearDown 接口classHashTestEnv2:publictesting::Test{public:staticvoidSetUpTestCase(){std::cout<<"环境 2 第一个 TEST 之前被调用,进行总体环境配置\n ";}staticvoidTearDownTestCase(){std::cout<<"环境 2 最后一个 TEST 之后被调用,进行总体环境清理\n ";}virtualvoidSetUp()override{std::cout<<"环境 2 测试前:提前准备数据!!\n";dict.insert(std::make_pair("bye","再见"));dict.insert(std::make_pair("see you","再见"));}virtualvoidTearDown()override{std::cout<<"环境 2 测试结束后:清理数据!!\n";dict.clear();}public:std::unordered_map<std::string,std::string>dict;};TEST_F(HashTestEnv2,insert_test){std::cout<<"环境 2,中间测试\n";dict.insert(std::make_pair("hello","你好"));ASSERT_EQ(dict.size(),3);}TEST_F(HashTestEnv2,size_test){std::cout<<"环境 2,中间 size 测试\n";autoit=dict.find("hello");ASSERT_EQ(it,dict.end());ASSERT_EQ(dict.size(),2);}intmain(intargc,char*argv[]){testing::InitGoogleTest(&argc,argv);RUN_ALL_TESTS();return0;}运行结果:
ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$makeg++ -ocasecase.cc -std=c++11 -lgtest ltx@My-Xshell-8-Pro-Max-Ultra:~/rabbit-mq/demo/gtest$ ./case[==========]Running2tests from1testsuite.[----------]Globaltestenvironment set-up.[----------]2tests from HashTestEnv2 环境2第一个 TEST 之前被调用,进行总体环境配置[RUN]HashTestEnv2.insert_test 环境2测试前:提前准备数据!!环境2,中间测试 环境2测试结束后:清理数据!![OK]HashTestEnv2.insert_test(0ms)[RUN]HashTestEnv2.size_test 环境2测试前:提前准备数据!!环境2,中间 size 测试 环境2测试结束后:清理数据!![OK]HashTestEnv2.size_test(0ms)环境2最后一个 TEST 之后被调用,进行总体环境清理[----------]2tests from HashTestEnv2(0ms total)[----------]Globaltestenvironment tear-down[==========]2tests from1testsuite ran.(0ms total)[PASSED]2tests.