1、简介
GTest是google公司发布的一个跨平台的(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)C++单元测试框架。它提供了丰富的断言、致命和非致命判断、参数化、死亡测试等等。
使用gtest时,就是编写断言(assertions),断言语句会检测条件是否为真。一个断言可存在三种结果:success(成功),nonfatal failure(非致命失败),或 fatal failure(致命失败)。当出现致命失败时,终止当前函数;否则程序继续执行。
2、基本概念
要测试一个类或函数,我们需要对其行为做出断言。当一个断言失败时,Google Test会在屏幕上输出该代码所在的源文件及其所在的位置行号,以及错误信息。也可以在编写断言时,提供一个自定义的错误信息,这个信息在失败时会被附加在Google Test的错误信息之后。
断言常常成对出现,它们都测试同一个类或者函数,但对当前功能有着不同的效果。ASSERT_*版本的断言失败时会产生致命失败,并结束当前函数。EXPECT_*版本的断言产生非致命失败,而不会中止当前函数。通常更推荐使用EXPECT_*断言,因为它们运行一个测试中可以有不止一个的错误被报告出来。但如果在编写断言如果失败,就没有必要继续往下执行的测试时,你应该使用ASSERT_*断言。 因为失败的ASSERT_*断言会立刻从当前的函数返回,可能会跳过其后的一些的清洁代码,这样也许会导致空间泄漏。
3、下载与安装
下载源码:
https://github.com/google/googletest
解压:
unzip googletest-release-1.10.0.zip
编译:
cmake CMakeLists.txt
生成静态库:
make
拷贝到系统目录(不同版本位置略有不同):
sudo cp libgtest*.a /usr/lib
sudo cp –a include/gtest /usr/include
验证:
例:gtest.cpp
#include<gtest/gtest.h>
using namespace std;
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();
return 0;
}
编译:
g++ gtest.cpp -lgtest -lpthread
运行:./a.out,结果如下:
4、GTest的断言
gtest 使用一系列断言的宏来检查值是否符合预期,主要分为两类:ASSERT 和 EXPECT。EXPECT_*和ASSERT_*的区别:EXPECT_*失败时,案例继续往下执行;ASSERT_*失败时,直接在当前函数中返回,当前函数中ASSERT_*后面的语句将不会执行,退出当前函数,并非退出当前案例。
(1)布尔值检查
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_TRUE(condition); | EXPECT_TRUE(condition); | 期待结果是true |
ASSERT_FALSE(condition); | EXPECT_FALSE(condition); | 期待结果是false |
(2)数值型数据检查
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_EQ(expected, actual); | EXPECT_EQ(expected, actual); | expected == actual |
ASSERT_NE(val1, val2); | EXPECT_NE(val1, val2); | val1 != val2 |
ASSERT_LT(val1, val2); | EXPECT_LT(val1, val2); | val1 < val2 |
ASSERT_LE(val1, val2); | EXPECT_LE(val1, val2); | val1 <= val2 |
ASSERT_GT(val1, val2); | EXPECT_GT(val1, val2); | val1 > val2 |
ASSERT_GE(val1, val2); | EXPECT_GE(val1, val2); | val1 >= val2 |
(3)字符串比较
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_STREQ(expected_str, actual_str); | EXPECT_STREQ(expected_str, actual_str); | 两个C字符串有相同的内容 |
ASSERT_STRNE(str1, str2); | EXPECT_STRNE(str1, str2); | 两个C字符串有不同的内容 |
ASSERT_STRCASEEQ(expected_str, actual_str); | EXPECT_STRCASEEQ(expected_str, actual_str); | 两个C字符串有相同的内容,忽略大小写 |
ASSERT_STRCASENE(str1, str2); | EXPECT_STRCASENE(str1, str2); | 两个C字符串有不同的内容,忽略大小写 |
(4)异常检查
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_THROW(statement, exception_type); | EXPECT_THROW(statement, exception_type); | statement throws an exception of the given type |
ASSERT_ANY_THROW(statement); | EXPECT_ANY_THROW(statement); | statement throws an exception of any type |
ASSERT_NO_THROW(statement); | EXPECT_NO_THROW(statement); | statement doesn't throw any exception |
(5)浮点型检查
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_FLOAT_EQ(expected, actual); | EXPECT_FLOAT_EQ(expected, actual); | the two float values are almost equal |
ASSERT_DOUBLE_EQ(expected, actual); | EXPECT_DOUBLE_EQ(expected, actual); | the two double values are almost equal |
对相近的两个数比较:
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_NEAR(val1, val2, abs_error); | EXPECT_NEAR(val1, val2, abs_error); | the difference between val1 and val2 doesn't exceed the given absolute error |
(6)此外还有类型检查、谓词检查等
(
5、事件机制
(1)全局事件
要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp()和TearDown()方法。
- SetUp()方法在所有案例执行前执行
- TearDown()方法在所有案例执行后执行
还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。
(2)TestSuite事件
我们需要写一个类,继承testing::Test,然后实现两个静态方法
- SetUpTestCase() 方法在第一个TestCase之前执行
- TearDownTestCase() 方法在最后一个TestCase之后执行
在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite。
(3)TestCase事件
TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:
- SetUp()方法在每个TestCase之前执行
- TearDown()方法在每个TestCase之后执行
例:
#include <gtest/gtest.h>
#include <map>
#include <iostream>
using namespace std;
class Student{
public:
Student(){
age=0;
}
Student(int a){
age=a;
}
void print(){
cout<<"*********** "<<age<<" **********"<<endl;;
}
private:
int age;
};
class FooEnvironment : public testing::Environment{
public:
virtual void SetUp() {
cout << "Foo FooEnvironment SetUP" << std::endl;
}
virtual void TearDown() {
cout << "Foo FooEnvironment TearDown" << std::endl;
}
};
static Student *s;
class TestMap : public testing::Test
{
public:
static void SetUpTestCase() {
cout<<"SetUpTestCase()"<<endl;
s=new Student(23);
}
static void TearDownTestCase(){
delete s;
cout<<"TearDownTestCase()"<<endl;
}
void SetUp() {
cout<<"SetUp() is running"<<endl;
}
void TearDown() {
cout<<"TearDown()"<<endl;
}
};
TEST_F(TestMap, Test1)
{
s->print();
}
int main(int argc, char** argv)
{
testing::AddGlobalTestEnvironment(new FooEnvironment);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
编译:
g++ testSuit.cpp -lgtest -lpthread -o testSuit
运行:
./a.out
6、参数化
当考虑多次要为被测函数传入不同的值的情况时,可以按下面的方式去测试。必须添加一个类,继承testing::TestWithParam<T>。其中T就是你需要参数化的参数类型,如下面的案例是int型参数。(官方文档上的案例)
例:
#include<gtest/gtest.h>
// Returns true iff n is a prime number.
bool IsPrime(int n)
{
// Trivial case 1: small numbers
if (n <= 1) return false;
// Trivial case 2: even numbers
if (n % 2 == 0) return n == 2;
// Now, we have that n is odd and n >= 3.
// Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2) {
// We only have to try i up to the squre root of n
if (i > n/i) break;
// Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0) return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
class IsPrimeParamTest : public::testing::TestWithParam<int>
{
};
TEST_P(IsPrimeParamTest, HandleTrueReturn)
{
int n = GetParam();
EXPECT_TRUE(IsPrime(n));
}
//被测函数须传入多个相关的值
INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
7、命令行参数
testing::InitGoogleTest(&argc,argv):gtest的测试案例允许接收一系列的命令行参数,将命令行参数传递给gtest,进行一些初始化操作。gtest的命令行参数非常丰富。
对于运行参数,gtest提供了三种设置的途径:
命令行参数:
- (1)--gtest_list_tests:使用这个参数时,将不会执行里面的测试案例,而是输出一个案例的列表;
- (2)--gtest_filter:对执行的测试案例进行过滤,支持通配符;
- (3)--gtest_also_run_disabled_tests:执行案例时,同时也执行被置为无效的测试案例;
- (4)--gtest_repeat=[COUNT]:设置案例重复运行次数;
- (5)--gtest_color=(yes|no|auto):输出命令行时是否使用一些五颜六色的颜色,默认是auto;
- (6)--gtest_print_time:输出命令时是否打印每个测试案例的执行时间,默认是不打印的;
- (7)--gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH:将测试结果输出到一个xml中,如—gtest_output=xml:d:\foo.xml 指定输出到d:\foo.xml ,如果不是指定了特定的文件路径,gtest每次输出的报告不会覆盖,而会以数字后缀的方式创建;
- (8)--gtest_break_on_failure:调试模式下,当案例失败时停止,方便调试;
- (9)--gtest_throw_on_failure:当案例失败时以C++异常的方式抛出;
- (10)--gtest_catch_exceptions:是否捕捉异常,gtest默认是不捕捉异常的,这个参数只在Windows下有效。
8、死亡测试
这里的”死亡”指的是程序的奔溃。通常在测试的过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序奔溃,这个时候我们就要检查程序是否按照预期的方式挂掉,这也就是所谓的”死亡测试”。
编写死亡测试案例时,TEST的第一个参数,即test_case_name,请使用DeathTest后缀,原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。
死亡测试所用到的宏:
- (1)ASSERT_DEATH(参数1,参数2),程序挂了并且错误信息和参数2匹配,此时认为测试通过。如果参数2为空字符串,则只需要看程序挂没挂即可。
- (2)ASSERT_EXIT(参数1,参数2,参数3),语句停止并且错误信息和被提前给的信息匹配。
例:
#include <gtest/gtest.h>
using namespace std;
int func()
{
int *ptr = NULL;
*ptr = 100;
return 0;
}
TEST(FunDeathTest, Nullptr)
{
ASSERT_DEATH(func(), "");
}
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
输出:
另:失败的案例
将上例中的func()换成如下:
int func()
{
int *ptr = new int(99);
return 0;
}
测试结果如下:
9、另外
(1)TEST(test_case_name, test_name)
TEST宏的作用是创建一个简单测试,它定义了一个测试函数,在这个函数里可以使用任何C++代码并使用提供的断言来进行检查。
多个测试场景需要相同数据配置的情况,用TEST_F。
(2)RUN_ALL_TESTS():运行所有测试案例。
(3)可以通过操作符"<<"将一些自定义的信息输出,如在EXPECT_EQ(v1, v2)<< "thisis a error! "
(4)testing::AddGlobalTestEnvironment(newFooEnvironment):在main函数中创建和注册全局环境对象。
10、实例
#include<iostream>
using namespace std;
#include<gtest/gtest.h>
struct LinkNode
{
int _data;
LinkNode *_next;
LinkNode(const int& data)
:_data(data)
,_next(NULL)
{}
};
class Link
{
public:
Link()
:pHead(new LinkNode(0))
{}
void PushBack(const int& data)
{
if(pHead == NULL)
return ;
LinkNode *newNode=new LinkNode(data);
if(pHead->_next == NULL){ //第一次插入结点
pHead->_next=newNode;
}
else{ //找到最后一个结点直接尾插
LinkNode *cur=pHead->_next;
while(cur->_next){
cur=cur->_next;
}
cur->_next=newNode;
}
}
void PopBack()
{
if(pHead == NULL)
return ;
LinkNode *cur=pHead;
LinkNode *prev=NULL;
while(cur->_next)
{
prev=cur;
cur=cur->_next;
}
prev->_next=NULL;
delete cur;
}
LinkNode *FindNode(const int& data)
{
if(pHead == NULL)
return NULL;
LinkNode *cur=pHead->_next;
while(cur)
{
if(cur->_data == data)
return cur;
cur=cur->_next;
}
return NULL;
}
bool Delete(int data)
{
LinkNode *pos=FindNode(data);
if(pos == NULL)
return false;
LinkNode *cur=pHead->_next;
while(cur->_next != pos)
{
cur=cur->_next;
}
cur->_next=pos->_next;
delete pos;
return true;
}
void Destroy()
{
if(pHead == NULL)
return;
LinkNode *cur=pHead->_next;
while(cur)
{
LinkNode *del=cur;
cur=cur->_next;
delete del;
del=NULL;
}
delete pHead; //删除头结点
}
LinkNode *pHead;
};
class TestLink:public testing::Test
{
public:
virtual void SetUp()
{
cout<<"SetUp"<<endl;
for(int i=1;i<=5;i++){
link.PushBack(i);
}
}
virtual void TearDown()
{
cout<<"TearDown"<<endl;
link.Destroy();
}
Link link;
};
TEST_F(TestLink,PushBack)
{
ASSERT_FALSE(link.pHead == NULL);
link.PushBack(9);
LinkNode *res=link.FindNode(9);
ASSERT_FALSE(res == NULL);
}
TEST_F(TestLink,PopBack)
{
for(int i=1;i<=5;i++){
link.PopBack();
}
}
TEST_F(TestLink,FindNode)
{
ASSERT_TRUE(link.FindNode(3));
ASSERT_TRUE(link.FindNode(2));
ASSERT_TRUE(link.FindNode(4));
ASSERT_TRUE(link.FindNode(5));
ASSERT_TRUE(link.FindNode(1));
ASSERT_FALSE(link.FindNode(7));
}
TEST_F(TestLink,Delete)
{
ASSERT_FALSE(link.pHead == NULL);
ASSERT_TRUE(link.Delete(3) == true);
ASSERT_TRUE(link.Delete(9) == false);
}
int main(int argc,char *argv[])
{
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)