开源项目学习(7) ---- Google Gtest
目录
- Gtest 简介
- Ubuntu 下的安装和使用
- Gtest 运行参数
- Gtest 断言
- Gtest 添加事件
- Gtest 多参数测试
Gtest 简介
Gtest
是一个跨平台的(Linux
、Mac OS X
、Windows
、Cygwin
、Windows CE
and Symbian
) C++
单元测试框架,由google
公司发布,gtest
是为在不同平台上为编写 C++
测试而生成的,它提供了丰富的断言、致命和非致命判断、参数化、死亡测试等等
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)为 FooTes
t的案例,但是除了FooTest.Bar
这个案例
Gtest 断言
gtest
中断言的宏可以分为两类,一类是 ASSERT
宏,另一类就是 EXPECT
宏了
ASSERT_
系列:如果当前点检测失败则退出当前测试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 thanEXPECT_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 否则 AssertASSERT_TRUE(a == b);// 必须是 false 才能 pass 否则 AssertASSERT_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
.
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::Testprotected: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"// 使用 int 类型的变量作为测试参数
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++单元测试框架