一. gtest 介绍

gtest 是一个跨平台的 C++ 单元测试框架,由 google 公司发布。gtest 是为了在不同平台上为编写 C++ 单元测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化等等测试所需的宏,以及全局测试,单元测试组件。

二. gtest 安装

sudo apt-get install libgtest-dev

在环境搭建中已经内置 gtest,这里不需要再次安装了。

三. gtest 使用

1. 头文件和链接库

在这里插入图片描述

#include <gtest/gtest.h>

程序编译时库的链接

-lgtest

2. 框架初始化接口

int main(int argc, char *argv[])
{
	testing::InitGoogleTest(&argc, argv);
	return 0;
}

3. 调用测试样例

RUN_ALL_TESTS();

4. TEST 宏

// 这里不需要双引号,且同测试下多个测试样例不能同名
TEST(测试名称, 测试样例名称)

TEST_F(test_fixture, test_name)
  • TEST:主要用来创建一个简单测试,它定义了一个测试函数,在这个函数中可以使用任何 C++ 代码并且使用框架提供的断言进行检查。
  • TEST_F:主要用来进行多样测试,适用于多个测试场景如果需要相同的数据配置的情况,即相同的数据测不同的行为。

5. 断言宏

GTest 中的断言的宏可以分为两类:

  • ASSERT_系列:如果当前点检测失败则退出当前函数。
  • EXPECT_系列:如果当前点检测失败则继续往下执行。

下面是经常使用的断言介绍:

// bool值检查
ASSERT_TRUE(参数)  // 期待结果是 true
ASSERT_FALSE(参数) // 期待结果是 false

// 数值型数据检查
ASSERT_EQ(参数1,参数2) // 传入的是需要⽐较的两个数 equal
ASSERT_NE(参数1,参数2) // not equal, 不等于才返回 true
ASSERT_LT(参数1,参数2) // less than, 小于才返回 true
ASSERT_GT(参数1,参数2) // greater than, 大于才返回 true
ASSERT_LE(参数1,参数2) // less equal, 小于等于才返回 true
ASSERT_GE(参数1,参数2) // greater equal, 大于等于才返回 true

四. gtest 使用样例

1. 目录结构

gtest/
|-- global.cc
|-- local1.cc
|-- local2.cc
|-- main.cc
|-- makefile

4.2 项目构建

# makefile
all: main global local1 local2
main: main.cc
	g++ -o $@ $^ -std=c++17 -lgtest
global: global.cc
	g++ -o $@ $^ -std=c++17 -lgtest
local1: local1.cc
	g++ -o $@ $^ -std=c++17 -lgtest
local2: local2.cc
	g++ -o $@ $^ -std=c++17 -lgtest

.PHONY: clean
clean:
	rm -f main global local1 local2

4.3 样例编写

// main.cc
#include <iostream>
#include <gtest/gtest.h>
#include <unordered_map>

// 1.定义测试用例
TEST(unordered_map, insert) {
    std::unordered_map<int, int> map;
    map.insert(std::make_pair(1, 1));
    map.insert(std::make_pair(2, 2));
    map.insert(std::make_pair(3, 3));
    EXPECT_EQ(map.size(), 3);
}
TEST(unordered_map, find) {
    std::unordered_map<int, int> map;
    map.insert(std::make_pair(1, 1));
    map.insert(std::make_pair(2, 2));
    map.insert(std::make_pair(3, 3));
    EXPECT_EQ(map.find(1)->second, 1);
}

int main(int argc, char* argv[])
{
    // 2.初始化测试环境
    testing::InitGoogleTest(&argc, argv);

    // 3.执行测试用例
    return RUN_ALL_TESTS();
}

在这里插入图片描述

5. gtest 事件机制

gtest 中的事件机制是指在测试前和测试后提供给用户自行添加操作的机制,而且该机制也可以让同一测试套件下的测试用例共享数据。gtest 框架中事件的结构层次:

在这里插入图片描述

  • 测试程序:一个测试程序只有一个 main 函数,也可以说是一个可执行程序是一个测试程序。该级别的事件机制是在程序的开始和结束执行。
  • 测试套件:代表一个测试用例的集合体,该级别的事件机制是在整体的测试案例开始和结束执行。
  • 测试用例:该级别的事件机制是在每个测试用例开始和结束都执用。

事件机制的最大好处就是能够为我们各个测试用例提前准备好测试环境,并在测试完毕后用于销毁环境,这样有个好处就是如果我们有一端代码需要进行多种不同方法的测试,则可以通过测试机制在每个测试用例进行之前初始化测试环境和数据,并在测试完毕后清理测试造成的影响。

gtest 提供了三种常见的的事件。

5.1 全局事件

针对整个测试程序。实现全局的事件机制,需要创建一个自己的类,然后继承 testing::Environment 类,然后分别进行 SetUp 和 TearDown 虚函数重写,同时在 main 函数内进行调用 testing::AddGlobalTestEnvironment(new MyEnvironment) 函数添加全局的事件机制。

  • SetUp() 函数是在所有测试用例的开始前执行。
  • TearDown() 函数是在所有测试用例的结束后执行。
// global.cc
#include <iostream>
#include <gtest/gtest.h>
#include <unordered_map>

// 全局变量
std::unordered_map<int, int> map;

// 1.定义全局测试环境类
class GlobalTestEnvironment : public testing::Environment {
public:
    virtual void SetUp() {
        std::cout << "执行于所有测试用例之前" << std::endl;
        map.insert(std::make_pair(1, 1));
        map.insert(std::make_pair(2, 2));
        map.insert(std::make_pair(3, 3));
    }
    virtual void TearDown() {
        std::cout << "执行于所有测试用例之后" << std::endl;
        map.clear();
    }
};

// 2.定义测试用例
TEST(GlobalTestEnvironment, remove) {
    EXPECT_EQ(map.size(), 3);
    map.erase(1);
}
TEST(GlobalTestEnvironment, find) {
    EXPECT_EQ(map.find(1), map.end());
}

int main(int argc, char* argv[])
{
    // 3.初始化测试环境
    testing::InitGoogleTest(&argc, argv);

    // 4.注册全局测试环境
    testing::AddGlobalTestEnvironment(new MyEnvironment);

    // 5.执行测试用例
    return RUN_ALL_TESTS();
}

在这里插入图片描述

5.2 TestCase 事件

针对一个个测试用例。测试用例的事件机制我们同样需要去创建一个类,继承 testing::Test 类,重写两个虚函数 SetUp 和 TearDown,测试用例的事件机制不需要像全局事件机制一样在 main 注册,而是需要将我们平时使用的 TEST 宏改为 TEST_F 宏。

  • SetUp() 函数是在每个测试用例的开始前执行。
  • TearDown() 函数是在每个测试用例的结束后执行。
  • 需要注意 TEST_F 的第一个参数是我们创建的类名,也就是当前测试套件的名称,这样在 TEST_F 宏的测试套件中就可以访问类中的成员了。
// local1.cc
#include <iostream>
#include <gtest/gtest.h>
#include <unordered_map>

// 1.定义局部测试环境类
class LocalTestSuite : public testing::Test {
public:
    virtual void SetUp() {
        std::cout << "执行于每个测试用例之前" << std::endl;
        map.insert(std::make_pair(1, 1));
        map.insert(std::make_pair(2, 2));
        map.insert(std::make_pair(3, 3));
    }
    virtual void TearDown() {
        std::cout << "执行于每个测试用例之后" << std::endl;
        map.clear();
    }
public:
    std::unordered_map<int, int> map;
};

// 2.定义测试用例
TEST_F(LocalTestSuite, find) {
    EXPECT_EQ(map.size(), 3);
    EXPECT_EQ(map.find(1)->second, 1);
    map.erase(1);
}
TEST_F(LocalTestSuite, erase) {
    map.erase(1);
    EXPECT_EQ(map.size(), 2);
}

int main(int argc, char* argv[])
{
    // 3.初始化测试环境
    testing::InitGoogleTest(&argc, argv);

    // 4.执行测试用例
    return RUN_ALL_TESTS();
}

在这里插入图片描述

5.3 TestSuite 事件

针对一个个测试套件。测试套件的事件机制的创建和测试套件的基本一样,不同地方在于实现两个静态函数 SetUpTestSuite 和 TearDownTestSuite (这里是隐藏)。

  • SetUpTestSuite() 函数是在测试套件第一个测试用例开始前执行。
  • TearDownTestSuite() 函数是在测试套件最后一个测试用例结束后执行。

也就是说,在 TestSuite/TestCase 事件中,每个测试用例,虽然它们同用一个事件环境类,可以访问其中的资源,但是本质上每个测试用例的环境都是独立的,这样我们就不用担心不同的测试用例之间会有数据上的影响了,保证所有的测试用例都使用相同的测试环境进行测试。

// local2.cc
#include <iostream>
#include <memory>
#include <gtest/gtest.h>
#include <unordered_map>

// 1.定义局部测试环境类
class LocalTestSuite : public testing::Test {
public:
    // TestSuite 事件
    static void SetUpTestSuite() {
        std::cout << "执行于所有测试用例之前" << std::endl;
        shared_map = new std::unordered_map<int, int>;
        shared_map->insert(std::make_pair(1, 1));
        shared_map->insert(std::make_pair(2, 2));
        shared_map->insert(std::make_pair(3, 3));
    }
    static void TearDownTestSuite() {
        std::cout << "执行于所有测试用例之后" << std::endl;
        delete shared_map;
        shared_map = nullptr;
    }
    // TestCase 事件
    virtual void SetUp() {
        std::cout << "执行于每个测试用例之前" << std::endl;
        // 拷贝共享数据,保证测试隔离
        local_map = *shared_map;
    }
    virtual void TearDown() {
        std::cout << "执行于每个测试用例之后" << std::endl;
        local_map.clear();
    }
public:
    // 套件级共享资源
    static std::unordered_map<int, int>* shared_map;
    // 用例级私有数据
    std::unordered_map<int, int> local_map;
};

// 静态成员定义
std::unordered_map<int, int>* LocalTestSuite::shared_map = nullptr;

// 2.定义测试用例
TEST_F(LocalTestSuite, find) {
    EXPECT_EQ(local_map.size(), 3);
    EXPECT_EQ(local_map.find(1)->second, 1);
    local_map.erase(1);
}
TEST_F(LocalTestSuite, erase) {
    local_map.erase(1);
    EXPECT_EQ(local_map.size(), 2);
}

int main(int argc, char* argv[])
{
    // 3.初始化测试环境
    testing::InitGoogleTest(&argc, argv);

    // 4.执行测试用例
    return RUN_ALL_TESTS();
}

在这里插入图片描述

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐