【C++ 脚手架】gtest 单元测试库的介绍与使用

gtest 介绍

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

一、gtest 安装

bash 复制代码
sudo apt-get install libgtest-dev

二、gtest 使用

2.1 包含头文件

cpp 复制代码
#include <gtest/gtest.h>

2.2 程序编译时的链接

makefile 复制代码
g++ main.cc -o main -lgtest

2.3 TEST 宏

cpp 复制代码
TEST(测试名称, 测试样例名称)
TEST_F(test_fixture,test_name)
  • TEST:主要用来创建一个简单测试,定义了一个测试函数,在这个函数中可以使用任何的 C++ 代码并使用框架提供的断言进行检查。 TEST 主要用于定义不需要复杂初始化或数据共享的测试用例,每个 TEST 都是独立的,互不干扰。

  • TEST_F:主要用来进行多样测试,TEST_F 用于需要共享初始化逻辑、清理逻辑或测试数据的场景,在多个测试用例中复用相同的测试环境。

⚠️ 测试用例和测试样例名称无需使用双引号,且同测试下多个测试样例不能同名。

2.4 断言宏

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

  • ASSERT_系列:如果当前点检测失败则退出当前函数。

  • EXPECT_系列:如果当前点检测失败则继续往下执⾏。

类别 断言宏 说明
布尔检查 EXPECT_TRUE(condition) 检查条件是否为真
EXPECT_FALSE(condition) 检查条件是否为假
数值比较 EXPECT_EQ(val1, val2) 检查 val1 == val2
EXPECT_NE(val1, val2) 检查 val1 != val2
EXPECT_LT(val1, val2) 检查 val1 < val2
EXPECT_GT(val1, val2) 检查 val1 > val2
EXPECT_LE(val1, val2) 检查 val1 <= val2
EXPECT_GE(val1, val2) 检查 val1 >= val2
字符串比较 EXPECT_STREQ(str1, str2) 检查两个 C 字符串内容相同
EXPECT_STRNE(str1, str2) 检查两个 C 字符串内容不同
异常检查 EXPECT_THROW(statement, exception_type) 检查语句是否抛出了指定类型的异常

2.5 初始化和调用

cpp 复制代码
#include <gtest/gtest.h>

int main(int argc , char* argv[]) {

    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

2.6 简单案例

gtest.cc

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <gtest/gtest.h>

TEST(unordered_map_test , insert) {
    std::unordered_map<int,int> hash;
    hash.insert({1, 1});
    hash.insert({2, 2});
    hash.insert({3, 3});
    ASSERT_EQ(hash.size() , 3);
}

int main(int argc , char* argv[]) {

    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Makefile

makefile 复制代码
main: gtest.cc
	g++ -o $@ $^ -std=c++17 -lgtest

输出结果:

bash 复制代码
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from unordered_map_test
[ RUN      ] unordered_map_test.insert
[       OK ] unordered_map_test.insert (0 ms)
[----------] 1 test from unordered_map_test (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 1 test.

三、gtest 事件机制

GTest 中的事件机制是指在测试前和测试后提供给⽤⼾⾃⾏添加操作的机制,⽽且该机制也可以让同⼀测试套件下的测试⽤例共享数据。GTest框架中事件的结构层次:

  • 测试程序 :测试程序是整个测试的可执行文件,包含所有测试套件和测试用例。一个测试程序通常对应一个可执行文件。对应 main 函数。该级别的事件机制是在程序的开始和结束执⾏。

  • 测试套件(TestSuite) :测试套件是一组相关的测试用例的集合,通常用于测试同一个类或同一个功能模块。测试套件通过 TEST() 宏的第一个参数来标识。该级别的事件机制是在整体的测试案例开始和结束执⾏

  • 测试用例(TestCase) :测试用例是最小的测试单元,代表一个具体的测试点。测试用例通过 TEST() 宏的第二个参数来标识。该级别的事件机制是在每个测试⽤例开始和结束都执⾏。

3.1 全局事件

全局事件是 Google Test 事件机制中级别最高的事件,通过继承 testing::Environment 类实现,然后实现两个虚函数 SetUpTearDown,同时,在 main 函数中调用 testing::AddGlobalTestEnvironment(new MyGlobalEnvironment),允许你在整个测试程序运行前后执行自定义的初始化和清理代码。

Setup()TearDown() 的执行时机:在所有测试运行之前/之后执行。

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <gtest/gtest.h>

std::unordered_map<int,int> hash;

class GlobalEnvironment : public testing::Environment {
    public:
        virtual void SetUp() override {
            std::cout << "在所有的测试之前执行" << std::endl;
            hash.insert({1, 1});
            hash.insert({2, 2});
            hash.insert({3, 3});
        };
        virtual void TearDown() override {
            std::cout << "在所有的测试之后执行" << std::endl;
            hash.clear();
        }
};

TEST(unordered_map_test , size) {
    EXPECT_EQ(hash.size() , 3); // true
}

TEST(unordered_map_test , erase) {
    hash.erase(1);
    EXPECT_EQ(hash.size() , 3); // false
    EXPECT_EQ(hash.size() , 2); // true
}

TEST(unordered_map_test , find) {
    EXPECT_EQ(hash.find(1) , hash.end());   // true
}

int main(int argc , char* argv[]) {
    testing::InitGoogleTest(&argc , argv);
    testing::AddGlobalTestEnvironment(new GlobalEnvironment);
    return RUN_ALL_TESTS();
}

输出结果:

bash 复制代码
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
在所有的测试之前执行
[----------] 3 tests from unordered_map_test
[ RUN      ] unordered_map_test.size
[       OK ] unordered_map_test.size (0 ms)
[ RUN      ] unordered_map_test.erase
gtest.cc:27: Failure
Expected equality of these values:
  hash.size()
    Which is: 2
  3

[  FAILED  ] unordered_map_test.erase (0 ms)
[ RUN      ] unordered_map_test.find
[       OK ] unordered_map_test.find (0 ms)
[----------] 3 tests from unordered_map_test (0 ms total)

[----------] Global test environment tear-down
在所有的测试之后执行
[==========] 3 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] unordered_map_test.erase

 1 FAILED TEST

3.2 TestSuite 事件

测试套件事件是在每个测试套件(Test Suite)开始和结束时触发的事件 ,允许你在测试套件级别执行初始化和清理操作。通过继承 testing::Test 类实现 ,然后实现 两个静态函数 SetUpTestCaseTearDownTestCase

⚠️:测试套件的事件机制不需要像全局事件机制一样在 main 函数中注册。

⚠️:TEST 宏需要改为 TEST_F

⚠️:TEST_F 的第一个参数必须要与创建类的名称完全一致

SetUpTestCase()TearDownTestCase() 的执行时机:在该测试套件第一个测试用例开始前/最后一个测试用例结束后执行

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <gtest/gtest.h>

class UnorderedMapEnvironmentTest : public testing::Test {
    public:
        static void SetUpTestCase() {
            std::cout << "在该测试套件第一个测试用例开始前执行" << std::endl;        
        }
        static void TearDownTestCase() {
            std::cout << "在该测试套件最后一个测试用例结束后执行" << std::endl;
        }
    public:
        std::unordered_map<int,int> hash;
};

TEST_F(UnorderedMapEnvironmentTest , size) {
    hash.insert({1, 1});            
    hash.insert({2, 2});    
    EXPECT_EQ(hash.size() , 2);
}

TEST_F(UnorderedMapEnvironmentTest , find) {
    // 这里是一个全新的 map
    // EXPECT_EQ(hash.find(2)->second ,  2);  // 段错误
    hash.insert({2, 2}); 
    EXPECT_EQ(hash.find(2)->second , 2);
}

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

输出结果:

bash 复制代码
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from UnorderedMapEnvironmentTest
在该测试套件第一个测试用例开始前执行
[ RUN      ] UnorderedMapEnvironmentTest.size
[       OK ] UnorderedMapEnvironmentTest.size (0 ms)
[ RUN      ] UnorderedMapEnvironmentTest.find
[       OK ] UnorderedMapEnvironmentTest.find (0 ms)
在该测试套件最后一个测试用例结束后执行
[----------] 2 tests from UnorderedMapEnvironmentTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

3.3 TestCase 事件

测试用例事件是在每个测试用例(Test Case)运行前后触发的事件,通过继承 Testing::Test 类,然后实现两个虚函数 SetUp()TearDown() 方法实现 ,确保每个测试用例都有独立的运行环境。

Setup()TearDown() 的执行时机:在该测试套件的每一个测试用例开始前/每一个测试用例结束后执行。

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <gtest/gtest.h>

class UnorderedMapEnvironmentTest : public testing::Test {
    public:
        static void SetUpTestCase() {
            std::cout << "在该测试套件第一个测试用例开始前执行" << std::endl;        
        }
        static void TearDownTestCase() {
            std::cout << "在该测试套件最后一个测试用例结束后执行" << std::endl;
        }
        virtual void SetUp() override {
            std::cout << "在该测试套件的每一个测试用例开始前执行" << std::endl;
            hash.insert({1, 1});
            hash.insert({2, 2});
            hash.insert({3, 3});
        };
        virtual void TearDown() override {
            std::cout << "在该测试套件的每一个测试用例结束后执行" << std::endl;
            hash.clear();
        }
    public:
        std::unordered_map<int,int> hash;
};

TEST_F(UnorderedMapEnvironmentTest , size) {   
    EXPECT_EQ(hash.size() , 3);
}

TEST_F(UnorderedMapEnvironmentTest , find) {
    EXPECT_EQ(hash.find(2)->second , 2);
}

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

输出结果:

bash 复制代码
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from UnorderedMapEnvironmentTest
在该测试套件第一个测试用例开始前执行
[ RUN      ] UnorderedMapEnvironmentTest.size
在该测试套件的每一个测试用例开始前执行
在该测试套件的每一个测试用例结束后执行
[       OK ] UnorderedMapEnvironmentTest.size (0 ms)
[ RUN      ] UnorderedMapEnvironmentTest.find
在该测试套件的每一个测试用例开始前执行
在该测试套件的每一个测试用例结束后执行
[       OK ] UnorderedMapEnvironmentTest.find (0 ms)
在该测试套件最后一个测试用例结束后执行
[----------] 2 tests from UnorderedMapEnvironmentTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.
相关推荐
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
郝学胜_神的一滴3 天前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
见过夏天3 天前
C++ 基础入门完全指南
c++
用户805533698035 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK5 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境6 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境6 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴7 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境9 天前
C++ 的Eigen 库全解析
c++
卷无止境9 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端