单元测试教程
本教程概述了单元测试方法,并讨论了 CLion 支持的四个测试框架:Google Test、Boost.Test、Catch2 和 Doctest。
在 CLion 中进行单元测试部分将指导您完成将框架包含到项目中的过程,并描述 CLion 的测试功能。
单元测试基础
单元测试旨在单独检查源代码的各个单元。 这里的 单元是可以独立测试的最小代码部分,例如自由函数或类方法。
单元测试的作用包括:
模块化您的代码
由于代码的可测试性取决于其设计,单元测试有助于将代码分解为专门的、易于测试的部分。
避免回归
当您拥有一组单元测试时,您可以反复运行它,以确保每次添加新功能或引入更改时,一切都 正常工作。
记录您的代码
运行、调试甚至仅仅阅读测试可以提供大量关于原始代码如何工作的信息,因此您可以将其用作隐式文档。
单个单元测试 是一种检查某些特定功能并具有明确通过/失败标准的方法。 单个测试的一般结构如下:
用于单元测试的 良好实践 包括:
为所有公开的函数(包括类构造函数和运算符)创建测试。
覆盖所有代码路径并检查普通情况和边界情况,包括那些带有错误输入数据的情况(请参阅 负面测试)。
确保每个测试独立运行,不会阻止其他测试的执行。
以运行顺序不影响结果的方式组织测试。
当测试用例在逻辑上相关或使用相同数据时,将它们分组是有用的。 套件 结合具有共同功能的测试(例如,为同一函数执行不同情况时)。 Fixture 类有助于为多个测试组织共享资源。 它们用于为组内的每个测试设置和清理环境,从而避免代码重复。
单元测试通常与 模拟结合使用。 Mock 对象 是测试目标的轻量级实现,用于当被测试功能包含复杂依赖项且难以使用真实对象构建可行的测试用例时。
框架
手动单元测试涉及许多例行操作:编写存根测试代码、实现 main() 、打印输出消息等。 单元测试框架不仅有助于自动化这些操作,还能让您从以下方面受益:
可管理的断言行为
使用框架,您可以指定单个检查的失败是否应取消整个测试的执行:除了常规的
ASSERT外,框架还提供EXPECT/CHECK宏,这些宏在失败时不会中断测试程序。各种检查器
检查器是用于比较预期结果和实际结果的宏。 测试框架提供的检查器通常具有可配置的严重性(警告、常规期望或要求)。 此外,它们可以包括浮点比较的容差,甚至预先实现的异常处理程序,用于检查在某些条件下是否引发异常。
测试组织
使用框架,可以轻松创建和运行按共同功能(套件)或共享数据(夹具)分组的测试子集。 此外,现代框架会自动注册新测试,因此您无需手动完成。
可自定义消息
框架负责测试输出:它们可以显示详细的描述性输出、用户定义的消息或仅简要的 通过/失败结果(后者对回归测试特别有用)。
XML 报告
大多数测试框架提供以 XML 格式导出结果的功能。 这在您需要将结果进一步传递到持续集成系统(如 TeamCity或 Jenkins )时非常有用。
在 CLion 中进行单元测试
CLion 对 Google Test、Boost.Test、Catch2 和 Doctest 的集成包括
框架库的完整代码洞察,
专用的运行/调试配置,
用于运行或调试测试/套件/夹具并检查其状态的边栏图标,
专用的测试运行器,
以及测试和夹具类的代码生成(适用于 Google 测试)。
为您的项目设置测试框架
在本章中,我们将讨论如何将 Google Test、Boost.Test、Catch2 和 Doctest 框架添加到 CLion 中的项目,以及如何编写一组简单的测试。
作为示例,我们将使用 DateConverter 项目,您可以从 github repo克隆该项目。 该程序计算以公历格式给定的日期的绝对值,并将其转换为儒略历日期。 最初,该项目不包含任何测试——我们将逐步添加它们。 为了看到框架之间的差异,我们将使用所有四个框架执行相同的测试。
您可以在 DateConverter_withTests仓库中找到该项目的最终版本。 以下是项目结构的转换方式:

对于每个框架 ,我们将执行以下操作:
将框架添加到 DateConverter 项目。
创建两个测试文件, AbsoluteDateTest.cpp 和 ConverterTests.cpp 。 这些文件将为每个框架命名相似,并包含使用特定框架语法编写的测试代码。
现在让我们打开克隆的 DateConverter 项目,并按照以下选项卡中的说明操作:
包含 Google Test 框架
在 DateConverter 项目根目录下创建一个 Google 测试文件夹。 在其中为框架文件创建另一个文件夹。 在我们的示例中,它是 Google_tests 和 Google_tests/lib 文件夹。
从官方 存储库下载 Google Test。 将 googletest-main 文件夹的内容解压到 Google_tests/lib 中。
在 Google_tests 文件夹中添加一个 CMakeLists.txt 文件(在项目树中右键点击并选择 )。 添加以下行:
project(Google_tests) add_subdirectory(lib) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})在 root CMakeLists.txt 脚本中,在末尾添加
add_subdirectory(Google_tests)行并重新加载项目。
添加 Google 测试
点击项目树中的 Google_tests 文件夹并选择 ,将其命名为 AbsoluteDateTest.cpp。
CLion 会提示将此文件添加到现有目标。 我们不需要这样做,因为我们将在下一步为此文件创建一个新目标。
为 ConverterTests.cpp 重复此步骤。
添加两个源文件后,我们可以为它们创建一个测试目标,并将其与
DateConverter_lib库链接。将以下行添加到 Google_tests/CMakeLists.txt :
# adding the Google_Tests_run target add_executable(Google_Tests_run ConverterTests.cpp AbsoluteDateTest.cpp) # linking Google_Tests_run with DateConverter_lib which will be tested target_link_libraries(Google_Tests_run DateConverter_lib) target_link_libraries(Google_Tests_run gtest gtest_main)将 Google Test 版本的检查从 AbsoluteDateTest.cpp和 ConverterTests.cpp复制到您的 AbsoluteDateTest.cpp 和 ConverterTests.cpp 文件中。
现在测试已准备好 运行。 例如,在 ConverterTests.cpp 左侧边栏中
DateConverterFixture声明旁边点击,然后选择 运行...。 我们将获得以下结果:

包含 Boost.Test 框架
在 DateConverter 项目根目录下为 Boost 测试创建一个文件夹。 在我们的示例中,它被称为 Boost_tests 。
在 Boost_tests 文件夹中添加一个 CMakeLists.txt 文件(在项目树中右键点击并选择 )。 添加以下行:
set (Boost_USE_STATIC_LIBS OFF) find_package (Boost REQUIRED COMPONENTS unit_test_framework) include_directories (${Boost_INCLUDE_DIRS})在 root CMakeLists.txt 脚本中,在末尾添加
add_subdirectory(Boost_tests)行并重新加载项目。
添加 Boost 测试
点击项目树中的 Boost_tests 并选择 ,将其命名为 AbsoluteDateTest.cpp。
CLion 会提示将此文件添加到现有目标。 我们不需要这样做,因为我们将在下一步为此文件创建一个新目标。
为 ConverterTests.cpp 重复此步骤。
添加两个源文件后,我们可以为它们创建一个测试目标,并将其与
DateConverter_lib库链接。 将以下行添加到 Boost_tests/CMakeLists.txt 中:add_executable (Boost_Tests_run ConverterTests.cpp AbsoluteDateTest.cpp) target_link_libraries (Boost_Tests_run ${Boost_LIBRARIES}) target_link_libraries (Boost_Tests_run DateConverter_lib)重新加载项目。
将 Boost.Test 版本的检查从 AbsoluteDateTest.cpp和 ConverterTests.cpp复制到项目中的相应源文件。
现在测试已准备好 运行。 例如,在 AbsoluteDateTest.cpp 左侧边栏中
BOOST_AUTO_TEST_SUITE(AbsoluteDateCheckSuite)旁边点击,然后选择 运行...。 我们将获得以下结果:

包含 Catch2 框架
按照 官方说明在系统上安装 Catch2。
在 DateConverter 项目根目录下为 Catch2 测试创建一个文件夹。 在我们的示例中,它被称为 Catch_tests 。
在 Catch_tests 文件夹中创建一个 CMakeLists.txt 文件(在项目树中右键点击该文件夹并选择 )。
我们将逐步填写此文件。 现在,在顶部添加一个命令:
find_package(Catch2 3 REQUIRED)在 root CMakeLists.txt 中,在末尾添加以下命令并重新加载项目:
add_subdirectory(Catch_tests)
添加 Catch2 测试目标
点击项目树中的 Catch_tests 并选择 ,将其命名为 AbsoluteDateTest.cpp。
点击 添加新目标:

将目标名称设置为 Catch_tests_run 并指定其位置 Catch_tests/CMakeLists.txt 。

点击 添加。
Catch_tests_run 目标将出现在列表中。 确保清除所有其他复选框:

点击 确定。
向项目中添加了一个新文件,并将以下命令添加到 Catch_tests/CMakeLists.txt 中:
add_executable(Catch_tests_run AbsoluteDateTest.cpp)在相同位置创建另一个文件并将其命名为 ConverterTests.cpp。 将其添加到
Catch_tests_run目标:
现在我们有两个源文件链接到测试目标:

打开 Catch_tests/CMakeLists.txt 脚本,并在
add_executable命令之后添加以下行:target_link_libraries(Catch_tests_run PRIVATE DateConverter_lib) target_link_libraries(Catch_tests_run PRIVATE Catch2::Catch2WithMain) include(Catch) catch_discover_tests(Catch_tests_run)重新加载项目。
添加测试代码并运行测试
将代码从 AbsoluteDateTest.cpp和 ConverterTests.cpp复制到相应的源文件。
注意,测试前面有
#include <catch2/catch_test_macros.hpp>现在测试已准备好运行。
运行测试的最快方法是点击
,位于
TEST_CASE旁边的侧边栏中:
CLion将在 测试运行器工具窗口中显示结果:

包含 Doctest 框架
在 DateConverter 项目根目录下为 Doctest 测试创建一个文件夹。 在我们的示例中,它被称为 Doctest_tests 。
下载 doctest.h头文件并将其放置在 Doctest_tests 文件夹中。
添加 Doctest 测试
点击项目树中的 Doctest_tests 并选择 ,将其命名为 AbsoluteDateTest.cpp。
CLion 会提示将此文件添加到现有目标。 我们不需要这样做,因为我们将在下一步为此文件创建一个新目标。
为 ConverterTests.cpp 重复此步骤。
将 CMakeLists.txt 文件添加到 Doctest_tests 文件夹中(在项目树中右键点击该文件夹并选择 )。 添加以下行:
add_executable(Doctest_tests_run ConverterTests.cpp AbsoluteDateTest.cpp) target_link_libraries(Doctest_tests_run DateConverter_lib)在 root CMakeLists.txt 中,在末尾添加
add_subdirectory(Doctest_tests)并重新加载项目。将 Doctest 版本的检查从 AbsoluteDateTest.cpp和 ConverterTests.cpp复制到项目中的相应源文件。
现在测试已准备好 运行。 例如,在 ConverterTests.cpp 左侧边栏中
TEST_CASE("Check various dates")旁边点击,然后选择 运行...。 我们将获得以下结果:

测试的运行/调试配置
测试框架为测试程序提供 main() 入口,因此可以在 CLion 中将其作为常规应用程序运行。 然而,我们建议使用专用的运行/调试配置,适用于 Google Test、 Boost.Test、 Catch2和 Doctest。 这些配置包括与测试相关的设置,并让您受益于内置的测试运行器(如果将测试作为常规应用程序运行,则无法使用)。
为测试创建运行/调试配置
转到 运行 | 编辑配置 ,点击
并选择一个特定框架的模板:

设置您的配置
根据框架,指定测试 模式、 suite 或 tags (适用于 Catch2)。 字段中提供了自动补全功能,以帮助您快速填写:

在指定测试模式时,您可以使用通配符。 例如,设置以下模式以仅运行示例项目中的
PlusOneDiff和PlusFour_Leap测试:
在配置设置的其他字段中,您可以设置环境变量或命令行选项。 例如,在 程序参数 字段中,您可以为 Catch2 测试设置
-s以强制通过的测试显示完整输出,或设置--gtest_repeat以多次运行 Google 测试:
输出将如下所示:
Repeating all tests (iteration 1) ... Repeating all tests (iteration 2) ... Repeating all tests (iteration 3) ...
测试的边栏图标
在 CLion 中,有 多种方式启动测试的运行/调试会话,其中一种是使用特殊的边栏图标。 这些图标帮助快速运行或调试单个测试或整个套件/夹具:

侧边栏图标还显示测试结果(如果已可用):成功 或失败
。
当您使用侧边栏图标运行测试/套件/夹具时,CLion 会创建相应类型的 临时运行/调试配置。 您可以在列表中看到这些配置以灰色显示。 要保存临时配置,请在 对话框中选择它并按 :

测试运行器
当您运行测试配置时,结果(以及过程)会显示在测试运行器窗口中,其中包括:
进度条 ,显示已执行测试的百分比,
树视图 ,显示所有正在运行的测试及其状态和持续时间,
测试的 output 流,
工具栏 ,提供重新运行失败的
测试、导出
或打开自动保存的先前结果
的选项,按字母顺序排序测试
以便轻松找到特定测试,或按持续时间排序
以了解哪个测试运行时间比其他测试更长。

Google 测试的代码生成
如果您使用 Google Test 框架,CLion 的 生成 菜单可以帮助您节省编写测试代码的时间。 在包含 gtest 的测试文件中,按 Alt+Insert 以查看代码生成选项。
当从夹具调用时,菜单还包括 SetUp 方法 和 TearDown 方法:

对于夹具测试,代码生成将 TEST() 宏转换为适当的 TEST_F()、 TEST_P()、 TYPED_TEST() 或 TYPED_TEST_P() (请参阅 类型化测试)。
其他功能
测试宏的快速文档
为了帮助您探索测试框架提供的宏, 快速文档弹出窗口 (Ctrl+Q )显示最终的宏替换并正确格式化。 它还会突出显示结果替换中使用的字符串和关键字:

显示测试列表
为了减少初始索引的时间,CLion 使用延迟测试检测。 这意味着测试在您打开某些测试文件或运行/调试测试配置之前会被排除在索引之外。 要检查当前为您的项目检测到的测试,请从 调用 显示测试列表。 请注意,调用此操作不会触发索引。