一、GTest简介
GoogleTest是一个单元测试的框架。
单元测试:
单元测试在函数在整个工程运行起来之前,对该函数进行测试,来判断当前函数能否达到预期的效果。
使用GoogleTest:
1.将GoogleTest编译为库;
2.在代码中链接这个库。
二、GTest编译安装
1.源代码编译:
#下载gtest,release-1.8.0
git clone https://github.com/google/googletest
# gtest编译
cd googletest
#生成Makefile文件(先安装cmake,brew install cmake),继续输入命令编译:
cmake CMakeLists.txt
#执行make,生成两个静态库:libgtest.a libgtest_main.a
make
#拷贝到系统目录,注意,如果下诉目录位置在不同版本位置有变动,用find . -name "libgtest*.a" 找到位置
sudo cp libgtest*.a /usr/lib
sudo cp –a include/gtest/ /usr/include
2.检查是否安装成功
可以写一个简单的测试代码gtest.cpp,如下:
#include<gtest/gtest.h>
int add(int a,int b){
return a+b;
}
TEST(testCase,test0){
EXPECT_EQ(add(2,3),5);
}
int main(int argc,char **argv){
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
在终端中编译该文件,并运行:
g++ -std=c++11 gtest.cpp -lgtest -lpthread -o gtest.out
./gtest.out
三、GTest使用
单元测试(Unit Test)是对软件基本组成单元(如函数或是一个类的方法)进行的测试,是开发者编写的一小段代码,用于检验被测试代码一个很小的、很明确的功能是否正确。
在gtest中,一个测试用例(test case)可以包含一个或多个测试。一个测试程序可以包含多个测试用例。
可以通过操作符”<<”将一些自定义的信息输出,如:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
1.定义测试函数
应用 googletest 编写单元测试时,使用 TEST() 宏来声明测试函数:
TEST(GlobalConfigurationTest, configurationDataTest)
2.断言/宏测试
gtest中,断言是用以检查条件是否为真。
ASSERT_* 版本的断言失败时会产生致命失败,并结束当前函数;
EXPECT_* 版本的断言失败时产生非致命失败,但不会中止当前函数。
3.事件机制
全局事件
要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。
-
SetUp()方法在所有案例执行前执行
-
TearDown()方法在所有案例执行后执行
还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。
TestSuite事件
我们需要写一个类,继承testing::Test,然后实现两个静态方法
-
SetUpTestCase() 方法在第一个TestCase之前执行
-
TearDownTestCase() 方法在最后一个TestCase之后执行
在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite。
TestCase事件
TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:
-
SetUp()方法在每个TestCase之前执行
-
TearDown()方法在每个TestCase之后执行
四、例程
1.一个超简单的 googletest 单元测试实例
#include<gtest/gtest.h>
int add(int a,int b){
return a+b;
}
TEST(testCase,test0){
EXPECT_EQ(add(2,3),5);
}
int main(int argc,char **argv){
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
2.一个较完整的 googletest 单元测试实例
// Configure.h
#pragma once
#include <string>
#include <vector>
class Configure
{
private:
std::vector<std::string> vItems;
public:
int addItem(std::string str);
std::string getItem(int index);
int getSize();
};
// Configure.cpp
#include "Configure.h"
#include <algorithm>
/**
* @brief Add an item to configuration store. Duplicate item will be ignored
* @param str item to be stored
* @return the index of added configuration item
*/
int Configure::addItem(std::string str)
{
std::vector<std::string>::const_iterator vi=std::find(vItems.begin(), vItems.end(), str);
if (vi != vItems.end())
return vi - vItems.begin();
vItems.push_back(str);
return vItems.size() - 1;
}
/**
* @brief Return the configure item at specified index.
* If the index is out of range, "" will be returned
* @param index the index of item
* @return the item at specified index
*/
std::string Configure::getItem(int index)
{
if (index >= vItems.size())
return "";
else
return vItems.at(index);
}
/// Retrieve the information about how many configuration items we have had
int Configure::getSize()
{
return vItems.size();
}
// ConfigureTest.cpp
#include <gtest/gtest.h>
#include "Configure.h"
TEST(ConfigureTest, addItem)
{
// do some initialization
Configure* pc = new Configure();
// validate the pointer is not null
ASSERT_TRUE(pc != NULL);
// call the method we want to test
pc->addItem("A");
pc->addItem("B");
pc->addItem("A");
// validate the result after operation
EXPECT_EQ(pc->getSize(), 2);
EXPECT_STREQ(pc->getItem(0).c_str(), "A");
EXPECT_STREQ(pc->getItem(1).c_str(), "B");
EXPECT_STREQ(pc->getItem(10).c_str(), "");
delete pc;
}
main()函数:
#include <gtest/gtest.h>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
// Runs all tests using Google Test.
return RUN_ALL_TESTS();
}
最后,将所有测试代码及main.cpp编译并链接到目标程序中。
此外,在运行可执行目标程序时,可以使用 --gtest_filter 来指定要执行的测试用例,如:
./foo_test 没有指定filter,运行所有测试;
./foo_test --gtest_filter=* 指定filter为*,运行所有测试;
./foo_test --gtest_filter=FooTest.* 运行测试用例FooTest的所有测试;
./foo_test --gtest_filter=*Null*:*Constructor* 运行所有全名(即测试用例名 + “ . ” + 测试名,如 GlobalConfigurationTest.noConfigureFileTest)含有"Null"或"Constructor"的测试;
./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行测试用例FooTest的所有测试,但不包括FooTest.Bar。
五、我自己学习 googletest 单元测试时写的例程:
//
// Created by toson on 19-3-8.
//
#include <gtest/gtest.h>
#include "shtf_sdk_interface.h"
//第一个参数是测试用例名,第二个参数是测试名:随后的测试结果将以"测试用例名.测试名"的形式给出
TEST(FaceImageInit_testCase, FaceImageInit_test){
//test data
FaceImage* image = new FaceImage;
char errorinfo[256];
//生成随机数用于测试
srand((unsigned)time(NULL));
//测试正数
image->width = rand();
image->height = rand();
image->size = rand();
image->bgr = (unsigned char*)rand();
cout << "image->width :" << image->width << endl;
cout << "image->height:" << image->height << endl;
cout << "image->size :" << image->size << endl;
FaceImageInit(image,errorinfo);
//断言是否完成程序该完成的功能
EXPECT_EQ(image->width, 0);
EXPECT_EQ(image->height, 0);
EXPECT_EQ(image->size, 0);
EXPECT_EQ(image->bgr, nullptr);
//测试负数
image->width = -rand();
image->height = -rand();
image->size = -rand();
image->bgr = (unsigned char*)-rand();
cout << "image->width :" << image->width << endl;
cout << "image->height:" << image->height << endl;
cout << "image->size :" << image->size << endl;
FaceImageInit(image,errorinfo);
EXPECT_EQ(image->width, 0);
EXPECT_EQ(image->height, 0);
EXPECT_EQ(image->size, 0);
EXPECT_EQ(image->bgr, nullptr);
delete image;
}
int main(int argc,char **argv){
testing::InitGoogleTest(&argc,argv); //用来处理Test相关的命令行开关,如果不关注也可不加
return RUN_ALL_TESTS();
}
/*----------------------------------------------------------*/
////在测试执行之前,系统会先执行TestEnvironment的SetUp()方法;
//// 在所有测试用例执行完之后,系统会执行TestEnvironment的TearDown()方法。
//// 另外,我们可以定义任意多个继承自testing::Environment的子类,以实现不同的全局事件。
//// 所有的子类的SetUp()按照我们调用testing::AddGlobalTestEnvironment添加它们的先后顺序执行,而TearDown()的执行顺序则与添加顺序相反。
//class ShtfSdkTnterfaceTestEnvironment : public testing::Environment{ //我的全局环境设置
//public:
// virtual void SetUp() //在所有测试启动之前需要做的操作
// {
// ;//std::cout << "Foo FooEnvironment SetUP" << std::endl;
// }
// virtual void TearDown() //在所有测试运行结束后需要做的操作
// {
// ;//std::cout << "Foo FooEnvironment TearDown" << std::endl;
// }
//// TestEnvironment(){ //在SetUp()之前
//// cout<<"TestEnvironment()"<<endl;
//// };
//// ~TestEnvironment(){ //在TearDown()之后
//// cout<<"~TestEnvironment()"<<endl;
//// };
//};
//
////在测试套件的第一个测试用例开始前,SetUpTestCase()函数会被调用,
//// 而在测试套件中的最后一个测试用例运行结束后,TearDownTestCase()函数会被调用。
//class TestMap:public testing::Test //套装例子
//{
//public:
// static void SetUpTestCase() //测试套件的第一个测试用例开始前,SetUpTestCase()函数会被调用
// {
// ;//cout<<"SetUpTestCase()"<<endl;
// s=new Student(23);
// }
// static void TearDownTestCase() //在测试套件中的最后一个测试用例运行结束后,TearDownTestCase()函数会被调用
// {
// delete s;
// ;//cout<<"TearDownTestCase()"<<endl;
// }
// void SetUp() //测试套件的每次测试用例开始前,SetUp()函数会被调用
// {
// ;//cout<<"SetUp(), running"<<endl;
// }
// void TearDown() //测试套件的每次测试用例运行结束后,TearDown()函数会被调用
// {
// ;//cout<<"TearDown(), stopping"<<endl;
// }
//// TestMap(){ //在SetUpTestCase()之后,在SetUp()之前
//// cout<<"TestMap()"<<endl;
//// };
//// ~TestMap(){ //在TearDown()之后,在SetUpTestCase()之前
//// cout<<"~TestMap()"<<endl;
//// };
//};
//
////第一个参数是测试用例名,第二个参数是测试名:随后的测试结果将以"测试用例名.测试名"的形式给出
//TEST_F(TestMap, Test1) //对TestMap进行测试,test1
//{
// s->print();
// Student s1(18);
// s1.print();
//// ASSERT_EQ(s1, (Student *)NULL);
//// ASSERT_NE(s1, (Student )NULL);
//}
//TEST_F(TestMap, Test2) //对TestMap进行测试,test2
//{
//// Student *s1;
// s->print();
//// ASSERT_NE(Student(23), NULL);
//// ASSERT_NE(s1, (Student *)NULL);
// ASSERT_EQ(0, 0);
//}
//
//int main(int argc, char** argv)
//{
// //在RUN_ALL_TESTS()之前,我们调用如下语句:
// // testing::AddGlobalTestEnvironment(new GlobalEnvent);
// // 将这个测试层面的的事件添加到事件列表即可。
// testing::AddGlobalTestEnvironment(new ShtfSdkTnterfaceTestEnvironment);
// testing::InitGoogleTest(&argc, argv); //用来处理Test相关的命令行开关,如果不关注也可不加
// return RUN_ALL_TESTS();
//}