GoogleTest单元测试学习

一、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方法。

  1. SetUp()方法在所有案例执行前执行

  2. TearDown()方法在所有案例执行后执行

还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。

TestSuite事件

我们需要写一个类,继承testing::Test,然后实现两个静态方法

  1. SetUpTestCase() 方法在第一个TestCase之前执行

  2. TearDownTestCase() 方法在最后一个TestCase之后执行

在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite。

TestCase事件

TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:

  1. SetUp()方法在每个TestCase之前执行

  2. 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();
//}
  • 2
    点赞
  • 31
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

Tosonw

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值