c 使用gtest框架编写单元测试的教程详解-kb88凯时官网登录

来自:网络
时间:2024-09-10
阅读:
免费资源网,https://freexyz.cn/

前言

gtest 是 google 开发的一个用于 c 的测试框架,广泛应用于编写和运行单元测试,并且支持任何类型的测试,而不仅仅是单元测试。

注意:

  • 本教程使用 cmake 启动并运行 googletest:需提前安装 cmake。
  • 术语:测试(test)、测试用例(test case)和测试套件(test suite)。

使用 cmake 启动并运行 gtest

1. 设置项目

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)

这个文件中包括了以下部分:

  1. cmake_minimum_required(version 3.14):指定了 cmake 的最低版本要求。
  2. project(my_project):定义了项目的名称。
  3. set(cmake_cxx_standard 14) 和 set(cmake_cxx_standard_required on):设置了 c 标准为 c 14,且要求编译器支持此标准。
  4. include(fetchcontent):包含了 cmake 的 fetchcontent 模块,用于从外部资源(如 github)获取依赖项。
  5. fetchcontent_declare(googletest url https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip):声明了对 googletest 的依赖,指定了下载地址。
  6. set(gtest_force_shared_crt on cache bool "" force):对于 windows 系统,防止覆盖父项目的编译器/链接器设置。
  7. fetchcontent_makeavailable(googletest):获取并使 googletest 可用。

2. 创建并运行二进制文件

将 gtest 声明为一个依赖项后,你就可以在自己的项目中使用 googletest 代码。

举例来说,在 my_project 目录中创建一个名为 hello_test.cc 的文件:

vim hello_test.cc 

内容如下:

#include 
 
// 展示一些基本断言。
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 模块来发现包含在二进制文件中的测试。

现在你可以依据下面指令构建和运行你的测试:

cmake -s . -b build
  • 告诉 cmake 在当前目录(-s .)中查找 cmakelists.txt 文件,并在指定的构建目录 build 中生成构建系统文件(-b build)。
cmake --build build
  • cmake 是调用 cmake 工具的命令。
  • --build 是用于告诉 cmake 执行构建操作的选项。
  • build 是构建目录的路径,指定了 cmake 在build 路径下执行构建操作。
cd build && ctest
  • cd build 进入构建目录。
  • ctest 会查找构建目录中的测试,并执行它们。

显示如下内容:

c  使用gtest框架编写单元测试的教程详解

恭喜!你成功地构建并运行了一个使用 googletest 的测试二进制文件。

gtest 入门

使用 gtest 时,首先要会编写断言(assertions),这些是检查条件是否为真的语句。

一个断言的结果可以是成功、非致命失败或致命失败【如果发生致命失败,它会中止当前函数;否则程序会正常继续执行】

测试使用断言来验证被测试代码的行为。如果一个测试崩溃或有一个失败的断言,那么它失败;否则它成功。

  • 一个测试套件(test suite)包含一个或多个测试(test)。应该将你的测试(test)分组到反映被测代码结构的测试套件(test suite)中。
  • 一个测试程序可以包含多个测试套件(test suite)。

接下来,我们将解释如何编写一个测试程序,从单个断言级别开始,逐步构建到测试和测试套件。

1 断言(assertions)

断言(assertions)是类似函数调用的宏。你可以通过对其行为进行断言来测试一个类或函数。当一个断言失败时,gtest 会打印断言的源文件和行号位置,以及一个失败消息。你还可以提供一个自定义的失败消息,它将附加到 gtest 的消息中。

这些断言成对出现,测试相同的事物,但对当前函数有不同的影响。

  • assert_* 版本在失败时会生成致命失败,并中止当前函数。
  • expect_* 版本生成非致命失败,不会中止当前函数。

通常情况下,优先使用 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 提供了一系列断言,用于以各种方式验证代码的行为。可以检查布尔条件,基于关系运算符比较值,验证字符串值、浮点值等等。甚至还有一些断言可以通过提供自定义谓词来验证更复杂的状态。

2 简单测试

  • 使用 test() 宏来定义和命名一个测试函数。这些是普通的 c 函数,不返回任何值。
  • 在这个函数中,除了你想包含的有效的 c 语句,使用各种 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。

在命名你的测试套件和测试时,应该遵循与命名函数和类相同的约定。

3 测试夹具:为多个测试使用相同的数据配置

test fixture(测试夹具)是指在测试运行前后,需要被执行的代码片段。

如果你发现自己在编写两个或更多操作相似数据的测试,可以使用测试夹具。这样可以为多个不同的测试重复使用相同的对象配置。

创建夹具的步骤:

  1. 从 testing::test 派生一个类。在类体开始处使用 protected:,因为我们希望从子类访问夹具成员。
  2. 在类中声明你需要使用的任何对象。
  3. 如果需要,编写一个默认构造函数或 setup() 函数,为每个测试准备对象。
    一个常见的错误是将 setup() 拼写为小写的 setup() - 在 c 11 中使用 override 确保拼写正确。
  4. 如果需要,编写一个析构函数或 teardown() 函数来释放你在 setup() 中分配的任何资源。
  5. 如果需要,为你的测试定义共享的子程序。
// 定义夹具类
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 ...
}
免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图