开源项目学习(7) ---- Google Gtest

Gtest 简介

Gtest 是一个跨平台的(LinuxMac OS XWindowsCygwinWindows CE and Symbian) C++ 单元测试框架,由google公司发布,gtest 是为在不同平台上为编写 C++ 测试而生成的,它提供了丰富的断言、致命和非致命判断、参数化、死亡测试等等

Ubuntu 下的安装和使用
  • 下载 Gtest 源代码
  • cd code/googletest/make 目录下 直接 make 即可
  • code/googletest/make 目录下会生成 gtest_main.asample1_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)包含 NullConstructor 的案例。
./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 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 类,实现里面的 SetUpTearDown 方法
    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::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"

// 使用 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++单元测试框架

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值