Gtest 简介
Gtest是一个跨平台的(Linux、Mac OS X、Windows、Cygwin、Windows CE and Symbian) C++单元测试框架,由google公司发布。gtest是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。
简而言之:Gtest测试框架可以在不同平台上为编写C++测试
Gtest 在 ubuntu 下的安装和使用
- 下载gtest 源代码
- cd code/googletest/make 目录下 直接make 即可
- 在 code/googletest/make 目录下会生成 gtest_main.a 和 sample1_unitest
sample1_unitest 是一个 gtest demo bin,对应于 googletest/samples 里面的
sample1_unittest.cc 文件
Gtest 运行参数
gtest 测试本身最终可以编译为一个可执行文件,gtest也提供了一系列的运行参数,使得我们可以对案例的执行进行一些有效的控制。
对于运行参数,gtest 提供了三种设置途径:
- 系统环境变量
- 命令行参数
- 代码中指定的 FLAG
因为提供了三种途径,就会有优先级的问题, 有一个原则是,最后设置的那个会生效。总结一下,通常情况下,比较理想的优先级为:
命令行参数 > 代码中指定FLAG > 系统环境变量
比如在代码中指定FLAG来设置输出,可以使用命令行参数 --gtest_output,可以使用 testing::GTEST_FLAG(output) = "xml",注意不需要加 --gtest 前缀,代码中的写法如下:
int _tmain(int argc, _TCHAR* argv[])
{
testing::GTEST_FLAG(output) = "xml:";
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
下面是对gtest 命令行参数的罗列,直接运行 gest demo --help ,也可以看到下面信息。
命令行参数 | 说明 |
---|---|
--gtest_list_tests | 输出所有的 testcase 列表 |
--gtest_filter | 对执行的测试案例进行过滤,支持通配符 |
--gtest_repeat=[COUNT] | 设置案例重复运行次数 |
--gtest_color=(yes/no/auto) | 输出是否使用多种颜色,默认是auto |
--gtest_print_time | 打印每个案例的执行时间,默认是不打印的 |
--gtest_repeat 参数详解:
命令行参数 | 说明 |
---|---|
--gtest_repeat=1000 | 重复执行1000次,即使中途出现错误。 |
--gtest_repeat=-1 | 无限次的执行 |
--gtest_repeat=1000 --gtest_break_on_failure | 重复执行1000次,并且在第一个错误发生时立即停止 |
--gtest_repeat=1000 --gtest_filter=DEMOA | 重复执行1000 次案例名称为 DEMOA 的测试用例 |
--gtest_output 参数详解
命令行参数 | 说明 |
---|---|
--gtest_output=xml: | 不指定输出路径时,默认为案例当前路径 |
--gtest_output=xml:d:\ | 指定输出到某个目录 |
-gtest_output=xml:d:\aaa.xml | 指定输出到d:\aaa.xml |
--gtest_filter
对执行的测试案例进行过滤,支持通配符
? 单个字符
* 任意字符
- 排除,如,-a 表示除了a
: 取或,如,a:b 表示a或b
./foo_test 没有指定过滤条件,运行所有案例
./foo_test --gtest_filter=使用通配符,表示运行所有案例
./foo_test --gtest_filter=FooTest.* 运行所有“测试案例名称(testcase_name)”为FooTest的案例
./foo_test --gtest_filter=Null:Constructor 运行所有“测试案例名称(testcase_name)”或“测试名称(test_name)”包含Null或Constructor的案例。
./foo_test --gtest_filter=-DeathTest. 运行所有非死亡测试案例。
./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行所有“测试案例名称(testcase_name)”为FooTest的案例,但是除了FooTest.Bar这个案例
Gtest 断言
gtest 中断言的宏可以分为两类,一类是 ASSERT 宏,另一类就是 EXPECT宏了
1、ASSERT_系列:如果当前点检测失败则退出当前测试
2、EXPECT_系列:如果当前点检测失败则继续往下执行
如果你对自动输出的错误信息不满意的话,也是可以通过operator<<能够在失败的时候打印日志,将一些自定义的信息输出。
ASSERT系列
宏类型 | 说明 |
---|---|
ASSERT_TRUE(参数) | 期待结果是true,否则 assert |
ASSERT_FALSE(参数) | 期待结果是false,否则 assert |
ASSERT_EQ(参数1,参数2) | 期待结果是equal 参数1 等于参数2, 否则 assert |
ASSERT_NE(参数1,参数2) | 期待结果是not equal,否则 assert |
ASSERT_LT(参数1,参数2) | 期待结果是less than,否则assert |
ASSERT_GT(参数1,参数2) | 期待结果是great than,否则assert |
ASSERT_LE(参数1,参数2) | 期待结果是less equal,否则assert |
ASSERT_GE(参数1,参数2) | 期待结果是greater euqal,否则 assert |
ASSERT_STREQ | 期待字符串相等,否则assert |
ASSERT_STRNE | 期待字符串不等,否则assert |
ASSERT_FLOAT_EQ | 两个浮点数是否相等 |
ASSERT_DOUBLE_EQ | 两个double 类型是否相等 |
ASSERT_NEAR(para1, para2,para3) | para1 para2 的误差不超过para3 的范围 |
tips: 在测试中输出自己添加的打印信息
#include "gtest/gtest.h"
int32_t Add(int32_t a, int32_t b)
{
return a + b;
}
int32_t Sub(int32_t a, int32_t b)
{
return a - b;
}
/*
EXPECTxx 在出错之后会继续执行
使用下面的方法可以在 测试中打出出log
*/
TEST(AssertTest, EXPECT_VERIFY)
{
EXPECT_EQ(Add(1,2), 3) << "result of Add function is " << Add(1,2);
EXPECT_NE(Add(1,2), 2) << "result of Add function is " << Add(1,2);
// less than
//EXPECT_LT(Add(1,2), 3);
// less than & equal
EXPECT_LE(Add(1,2), 3);
//greater than
EXPECT_GE(Add(1,2), 3);
//greater than & equal
//EXPECT_GT(Add(1,2), 3);
}
//ASSERT 在出错之后会立刻停止执行
TEST(AssertTest, ASSERT_VERIFY)
{
ASSERT_EQ(Add(1,2), 3) << "result of Add function is " << Add(1,2);
ASSERT_NE(Add(1,2), 2) << "result of Add function is " << Add(1,2);
// less than & equal
ASSERT_LE(Add(1,2), 3);
//greater than
//ASSERT_GE(Add(1,2), 3);
//greater than & equal
//ASSERT_GT(Add(1,2), 3);
bool val = true;
ASSERT_TRUE(val == true);
ASSERT_FALSE(val == false);
}
//判断字符串是否相等
TEST(AssertTest, ASSERT_STRINGVERIFY)
{
ASSERT_STREQ("hello", "hello");
ASSERT_STRNE("hello", "hellorr");
ASSERT_STRCASEEQ("hello", "HeLLO");
//ASSERT_STRCASENE("hello", "HeLLO");
}
TEST(AssertTest, FAIL)
{
//会继续向下执行
//ADD_FAILURE() << "sorry this case failure";
//FAIL();
}
TEST(AssertTest, Success)
{
SUCCEED();
}
int32_t AddThrow(int32_t a, int32_t b)
{
if(a == 0 && b ==0)
throw("a== 0 && b ==0");
return a - b;
}
TEST(AssertTest, ASSERT_THROW)
{
// 需要 throw 一个特定类型的 exception,否则会 Assert
//ASSERT_THROW(AddThrow(0,0), char*);
// 必须由 throw 才会 pass ,否会fail
//ASSERT_ANY_THROW(AddThrow(1,0));
// 必须是 NO_THROW 才会 pass ,否则会 assert 掉
//ASSERT_NO_THROW(AddThrow(0,0));
}
uint32_t a = 10;
uint32_t b = 10;
TEST(AssertTest, ASSERT_BOOL)
{
// 必须是 true 才能 pass 否则 Assert
ASSERT_TRUE(a == b);
// 必须是 false 才能 pass 否则 Assert
ASSERT_FALSE(a != b);
// 必须是 true 才能 pass 否则报错
EXPECT_TRUE(a == b);
// 必须是 FALSE 才能 pass 否则报错
//EXPECT_FALSE(a == b);
}
float a_f = 1.23;
float b_f = 1.23;
double c_d = 1.34;
double d_d = 1.34;
double e_d = 1.234;
double f_d = 1.256;
TEST(AssertTest, ASSERT_FLOAT)
{
ASSERT_FLOAT_EQ(a_f , b_f);
ASSERT_DOUBLE_EQ(c_d ,d_d);
EXPECT_FLOAT_EQ(a_f , b_f);
EXPECT_DOUBLE_EQ(c_d , d_d);
// para1 para2 的误差不超过 para3的范围,否则报错
ASSERT_NEAR(e_d, f_d, 0.1);
ASSERT_NEAR(e_d, f_d, 0.01);
}
Gtest 添加事件
Gtest 时间本质是一种更具体的测试框架,在这个框架下,你有机会来执行你定制的代码,来给测试用例准备和清除数据,总结 Gtest 事件分为下面三种:
-
全局事件
要实现全局事件,必须写一个类,继承testing::Environment类,
实现里面的SetUp和TearDown方法。
SetUp方法在所有案例执行前执行;
TearDown方法在所有案例执行后执行.
例如全局事件可以按照下列方式来使用:
除了要继承testing::Environment类,还要定义一个该全局环境的一个对象并将该对象添加到全局环境测试中去。 -
TestSuite事件
TestSuite 是一连串TestCase 的集合,在TestSuite 里又可以有多个测试项,TestSuite事件会在 TestSuite 前后运行自己的静态方法.
编写自己的TestSuite 类,继承testing::Test,然后实现两个静态方法:
SetUpTestCase方法在第一个TestCase之前执行;
TearDownTestCase方法在最后一个TestCase之后执行
在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite.
3.TestCase事件
TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:
SetUp() 方法在每个TestCase之前执行
TearDown() 方法在每个TestCase之后执行
参考代码:
#include "gtest/gtest.h"
#include <stdio.h>
using namespace std;
// SetUp 和 TearDown 会在所有测试之前执行一遍
class DemoTestEvent: public testing::Environment
{
public:
virtual void SetUp()
{
cout << "enter Setup function" << endl;
}
virtual void TearDown()
{
cout << "enter TearDown function" << endl;
}
};
float a_f = 1.23;
float b_f = 1.23;
TEST(AssertTest, ASSERT_FLOAT)
{
ASSERT_FLOAT_EQ(a_f , b_f);
}
int main(int argc, char **argv)
{
testing::AddGlobalTestEnvironment(new DemoTestEvent);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
class DemoTestSuite : public testing::Test
protected:
static void SetUpTestCase()
{
std::cout << "this is DemoTestSuite::SetUpTestCase" << endl;
}
static void TearDownTestCase()
{
std::cout << "this is DemoTestSuite::TearDownTestCase" << endl;
}
virtual void SetUp()
{
std::cout << "this is DemoTestSuite::SetUp" << endl;
}
virtual void TearDown()
{
std::cout << "this is DemoTestSuite::TearDown" << endl;
}
private:
std::string demo = "My DemoTestSuite";
};
TEST_F(DemoTestSuite, ASSERT_FLOAT)
{
ASSERT_FLOAT_EQ(a_f , b_f);
}
Gtest 参数化
我们测试时经常需要考虑给被测函数传入不同的值的情况。之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的,程序员都懒,都希望能够少写代码,多复用代码。Google的程序员也一样,他们考虑到了这个问题,并且提供了一个灵活的参数化测试的方案。
#include "gtest/gtest.h"
class Parametertest: public testing::TestWithParam<int>
{
public:
std::string Namepub = "Hello Parametertest public";
std::string getPriName(void);
private:
std::string Namepri = "Hello Parametertest private";
};
std::string Parametertest::getPriName()
{
return Namepri;
}
TEST_P(Parametertest, CheckPara)
{
auto temp = GetParam();
std:: cout << "Get Para" << temp << std::endl;
std:: cout << "Get private element " << Namepub << std::endl;
std:: cout << "Call public function " << getPriName() << std::endl;
}
INSTANTIATE_TEST_CASE_P(mytest, Parametertest, testing::Values(3, 5, 11, 23, 17));
INSTANTIATE_TEST_CASE_P(mytest, Parametertest, testing::Values(3, 5, 11, 23, 17));
就是表示测试参数的范围
第一个参数是测试案例的前缀,可以任意取。
第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:Parametertest
第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数。Google提供了一系列的参数生成的函数:
参数 | 说明 |
---|---|
Range(begin, end[, step]) | 范围在begin~end之间,步长为step,不包括end |
Values(v1, v2, ..., vN) | v1,v2到vN的值 |
ValuesIn(container) and ValuesIn(begin, end) | 从一个C类型的数组或是STL容器,或是迭代器中取值 |
Bool() | 取false 和 true 两个值 |
Combine(g1, g2, ..., gN) | 这个比较强悍,它将g1,g2,...gN进行排列组合,g1,g2,...gN本身是一个参数生成器,每次分别从g1,g2,..gN中各取出一个值,组合成一个元组(Tuple)作为一个参数。说明:这个功能只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1。 |
本篇博客参考了下面博客中的内容:
玩转Google开源C++单元测试框架
网友评论