gtest 是 Google 开发的一个用于 C++ 的测试框架,广泛应用于编写和运行单元测试,并且支持任何类型的测试,而不仅仅是单元测试。
本教程分成以下部分:
注意:
CMake 使用 CMakeLists.txt 来配置项目的构建系统【使用该文件设置项目,并声明对 gtest 的依赖】
首先,创建一个项目的目录:
mkdir my_project && cd my_project
接下来,将创建 CMakeLists.txt 文件并声明对 GoogleTest 的依赖。
在项目目录(my_project)中,创建一个名为 CMakeLists.txt 的文件:
vim CMakeLists.txt
其内容如下:
cmake_minimum_required(VERSION 3.14)
project(my_project)
# 设置 C++ 标准为 C++14
set(CMAKE_CXX_STANDARD 14)
# 强制要求编译器支持所选的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 包含 FetchContent 模块,用于从外部资源获取依赖项
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# 对于 Windows 系统:防止覆盖父项目的编译器/链接器设置
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# 使得 GoogleTest 可用
FetchContent_MakeAvailable(googletest)
这个文件中包括了以下部分:
cmake_minimum_required(VERSION 3.14)
:指定了 CMake 的最低版本要求。project(my_project)
:定义了项目的名称。set(CMAKE_CXX_STANDARD 14)
和 set(CMAKE_CXX_STANDARD_REQUIRED ON)
:设置了 C++ 标准为 C++14,且要求编译器支持此标准。include(FetchContent)
:包含了 CMake 的 FetchContent 模块,用于从外部资源(如 GitHub)获取依赖项。FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip)
:声明了对 GoogleTest 的依赖,指定了下载地址。set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
:对于 Windows 系统,防止覆盖父项目的编译器/链接器设置。FetchContent_MakeAvailable(googletest)
:获取并使 GoogleTest 可用。将 gtest 声明为一个依赖项后,你就可以在自己的项目中使用 GoogleTest 代码。
举例来说,在 my_project 目录中创建一个名为 hello_test.cc 的文件:
vim hello_test.cc
内容如下:
#include <gtest/gtest.h>
// 展示一些基本断言。
TEST(HelloTest, BasicAssertions) {
// 期望两个字符串不相等。
EXPECT_STRNE("hello", "world");
// 期望相等。
EXPECT_EQ(7 * 6, 42);
}
要构建代码,需要将以下内容添加到你的 CMakeLists.txt 文件末尾:
# 启用测试
enable_testing()
# 声明要测试的可执行文件
add_executable(
hello_test
hello_test.cc
)
# 链接 GoogleTest 主要库
target_link_libraries(
hello_test
GTest::gtest_main
)
# 包含 GoogleTest 模块
include(GoogleTest)
# 使用 gtest_discover_tests 函数来自动发现并添加测试
gtest_discover_tests(hello_test)
上述配置启用了 CMake 中的测试,声明了要构建的 C++ 测试二进制文件(hello_test),并将其链接到 GoogleTest(gtest_main)。 最后两行启用了 CMake 的测试运行器,使用 GoogleTest 的 CMake 模块来发现包含在二进制文件中的测试。
现在你可以依据下面指令构建和运行你的测试:
1.
cmake -S . -B build
-S .
)中查找 CMakeLists.txt 文件,并在指定的构建目录 build
中生成构建系统文件(-B build
)。2.
cmake --build build
cmake
是调用 CMake 工具的命令。--build
是用于告诉 CMake 执行构建操作的选项。build
是构建目录的路径,指定了 CMake 在build 路径下执行构建操作。3.
cd build && ctest
显示如下内容:
恭喜!你成功地构建并运行了一个使用 GoogleTest 的测试二进制文件。
使用 gtest 时,首先要会编写断言(assertions),这些是检查条件是否为真的语句。
一个断言的结果可以是成功、非致命失败或致命失败【如果发生致命失败,它会中止当前函数;否则程序会正常继续执行】
测试使用断言来验证被测试代码的行为。如果一个测试崩溃或有一个失败的断言,那么它失败;否则它成功。
接下来,我们将解释如何编写一个测试程序,从单个断言级别开始,逐步构建到测试和测试套件。
断言(assertions)是类似函数调用的宏。你可以通过对其行为进行断言来测试一个类或函数。当一个断言失败时,gtest 会打印断言的源文件和行号位置,以及一个失败消息。你还可以提供一个自定义的失败消息,它将附加到 gtest 的消息中。
这些断言成对出现,测试相同的事物,但对当前函数有不同的影响。
通常情况下,优先使用 EXPECT_*,因为它们允许在一个测试中报告多个失败。然而,如果在相关断言失败时继续执行不合理,则应该使用 ASSERT_*。 由于失败的 ASSERT_* 会立即返回当前函数,可能会跳过其后的清理代码,从而可能导致空间泄漏。根据泄漏的性质,如果除了断言错误外还出现堆检查器错误。 要提供自定义的失败消息,只需使用 << 运算符或一系列此类运算符将其流式传递到宏中。
【示例】使用 ASSERT_EQ 和 EXPECT_EQ 宏来验证值的相等性:
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;
}
任何可以流式传输到 ostream 的内容都可以流式传输到断言宏中 - 特别是 C 字符串和字符串对象。如果将宽字符串(wchar_t*、 TCHAR*在 Windows 的UNICODE 模式下,或者 std::wstring)流式传输到断言中,则在打印时会被转换为 UTF-8 编码。
gtest 提供了一系列断言,用于以各种方式验证代码的行为。可以检查布尔条件,基于关系运算符比较值,验证字符串值、浮点值等等。甚至还有一些断言可以通过提供自定义谓词来验证更复杂的状态。
TEST(TestSuiteName, TestName) {
... test body ...
}
TEST() 宏的第一个参数是测试套件(test suite)的名称,第二个参数是测试套件内的测试名称。两个名称都必须是有效的 C++ 标识符,并且不能包含下划线【测试的全名=其所属的测试套件+其单独的名称组成。来自不同测试套件的测试可以有相同的单独名称】
【示例】以一个简单的整数函数为例
int Factorial(int n); // 返回 n 的阶乘
此函数的测试套件可能如下:
// 测试 0 的阶乘
TEST(FactorialTest, HandlesZeroInput) {
// 期望 Factorial(0) 的结果是 1
EXPECT_EQ(Factorial(0), 1);
}
// 测试正数的阶乘
TEST(FactorialTest, HandlesPositiveInput) {
// 期望 Factorial(1) 的结果是 1
EXPECT_EQ(Factorial(1), 1);
// 期望 Factorial(2) 的结果是 2
EXPECT_EQ(Factorial(2), 2);
// 期望 Factorial(3) 的结果是 6
EXPECT_EQ(Factorial(3), 6);
// 期望 Factorial(8) 的结果是 40320
EXPECT_EQ(Factorial(8), 40320);
}
GoogleTest 按测试套件分组测试结果,因此逻辑上相关的测试应在同一个测试套件中;换句话说,它们的 TEST() 的第一个参数应该相同。
在上面的示例中,我们有两个测试,HandlesZeroInput 和 HandlesPositiveInput,它们属于同一个测试套件 FactorialTest。
在命名你的测试套件和测试时,应该遵循与命名函数和类相同的约定。
Test Fixture(测试夹具)是指在测试运行前后,需要被执行的代码片段。
如果你发现自己在编写两个或更多操作相似数据的测试,可以使用测试夹具。这样可以为多个不同的测试重复使用相同的对象配置。
创建夹具的步骤:
testing::Test
派生一个类。在类体开始处使用 protected:
,因为我们希望从子类访问夹具成员。SetUp()
函数,为每个测试准备对象。
一个常见的错误是将 SetUp()
拼写为小写的 Setup()
- 在 C++11 中使用 override
确保拼写正确。TearDown()
函数来释放你在 SetUp()
中分配的任何资源。// 定义夹具类
class MyTestFixture : public testing::Test {
protected:
// 在这里声明你的对象
int* myObject;
// 如果需要,编写构造函数或 SetUp() 函数
void SetUp() override {
myObject = new int(42); // 示例初始化
}
// 如果需要,编写析构函数或 TearDown() 函数
void TearDown() override {
delete myObject;
}
};
// 使用 TEST_F() 进行测试
TEST_F(MyTestFixture, Test1) {
// 可以在这里访问 myObject
EXPECT_EQ(*myObject, 42);
}
TEST_F(MyTestFixture, Test2) {
// 也可以在这里访问 myObject
EXPECT_NE(*myObject, 0);
}
使用夹具时,使用 TEST_F()
而不是 TEST()
,因为它允许你访问测试夹具中的对象和子程序:
TEST_F(TestFixtureClassName, TestName) {
... test body ...
}
本文参考GoogleTest 用户指南:GoogleTest User’s Guide | GoogleTest