C++单元测试教程

转自: https://www.jetbrains.com/help/clion/unit-testing-tutorial.html

本教程概述了单元测试方法,并讨论了CLion支持的四个框架:Google Test,Boost.Test,Catch2和Doctest。CLion中单元测试部分将指导您完成将这些框架包含到项目中的过程,并描述CLion提供的可帮助您进行单元测试的工具。

单元测试的基础

单元测试旨在分别检查源代码的各个单元。此处的单元是可以隔离进行测试的代码的最小部分,例如,自由函数或类方法。

单元测试有助于:

  • 模块化您的代码

    由于代码的可测试性取决于其设计,因此单元测试有助于将其分解为易于测试的专用部分。

  • 避免回归

    如果拥有一套单元测试,则可以迭代地运行它,以确保每次添加新功能或进行更改时,一切都能正常工作

  • 记录您的代码

    运行,调试甚至只是阅读测试都可以提供许多有关原始代码如何工作的信息,因此您可以将它们用作隐式文档。

 

单个单元测试是一种方法,检查一些特定的功能,并具有明确的合格/不合格标准。单个测试的通用结构如下所示:

 

单元测试的良好做法包括:

  • 为所有公开公开的功能(包括类构造函数和运算符)创建测试。

  • 涵盖所有代码路径,并检查琐碎的情况和边缘情况,包括那些输入数据不正确的情况(请参见否定测试)。

  • 确保每个测试独立运行,并且不会阻止其他测试的执行。

  • 以不会影响测试结果运行顺序的方式来组织测试。

将测试用例逻辑连接或使用相同的数据时,对它们进行分组很有用。套件将测试与通用功能结合在一起(例如,对同一功能执行不同案例时)。夹具类有助于组织用于多个测试的共享资源。它们用于为组内的每个测试设置和清理环境,从而避免代码重复。

单元测试通常与模拟结合在一起。模拟对象是测试目标的轻量级实现,在被测功能包含复杂的依赖关系且难以使用实际对象构造可行的测试用例时使用。

构架

手动单元测试涉及许多例程:编写存根测试代码,实现main(),打印输出消息等。单元测试框架不仅有助于自动化这些操作,而且还使您从以下方面受益:

  • 可管理的断言行为

    使用框架,您可以指定单项检查的失败是否应取消整个测试执行:与Regular一起ASSERT,框架提供EXPECT/CHECK了不会在失败时中断测试程序的宏。

  • 各种检查器

    Checkers是用于比较预期结果和实际结果的宏。测试框架提供的检查程序通常具有可配置的严重性(警告,定期期望或要求)。而且,它们可以包括浮点比较的容差,甚至可以包括在某些情况下检查异常引发的预先实现的异常处理程序。

  • 测试组织

    使用框架,可以轻松创建和运行按通用功能(套件)或共享数据(夹具)分组的测试子集。另外,现代框架会自动注册新测试,因此您无需手动进行。

  • 可自定义的消息

    框架负责测试输出:它们可以显示详细的描述性输出,用户定义的消息或仅简短的通过/失败结果(后者对于回归测试特别方便)。

  • XML报告

    大多数测试框架都以XML格式提供导出结果。当您需要将结果进一步传递给持续集成系统(例如TeamCityJenkins)时,这很有用。

有许多C ++的单元测试框架接下来,我们将重点介绍一些最受欢迎的工具:Google TestBoost.TestCatch2Doctest。所有这四个都集成在CLion中,但是在我们深入介绍集成细节之前,让我们简要介绍每个框架的要点。

谷歌测试

Google TestGoogle Mock是一对功能强大的单元测试工具:该框架具有可移植性,它包含一组致命的和非致命的断言,提供了创建固定装置和测试组的工具,提供了有用的消息,并将结果导出到XML。可能唯一的缺点是需要在项目中构建gtest / gmock才能使用它。

断言

在Google Test中,将检查条件是否为真的语句称为断言。非致命断言EXPECT_的名称带有前缀,而导致致命故障并中止执行的断言以开头ASSERT_。例如:

 

以下列出了Google Test中可用的一些断言(在此表中,ASSERT_给出了一个示例,可以使用进行切换EXPECT_):

逻辑上ASSERT_TRUE(condition)
ASSERT_FALSE(condition)
一般比较ASSERT_EQ(expected, actual) / ASSERT_NE(val1, val2)
ASSERT_LT(val1, val2) / ASSERT_LE(val1, val2)
ASSERT_GT(val1, val2) / ASSERT_GE(val1, val2)
浮点比较ASSERT_FLOAT_EQ(expected, actual)
ASSERT_DOUBLE_EQ(expected, actual)
ASSERT_NEAR(val1, val2, abs_error)
字符串比较ASSERT_STREQ(expected_str, actual_str) / ASSERT_STRNE(str1, str2)
ASSERT_STRCASEEQ(expected_str, actual_str) / ASSERT_STRCASENE(str1, str2)
异常检查ASSERT_THROW(statement, exception_type)
ASSERT_ANY_THROW(statement)
ASSERT_NO_THROW(statement)

此外,Google Test支持谓词断言,有助于使输出消息更具信息性。例如,EXPECT_EQ(a, b)可以使用谓词函数来代替和,该谓词函数检查ab是否相等并返回布尔结果。如果失败,则断言将打印函数参数的值:

谓词断言示例

输出

EXPECT_PRED2上面,predN是带有N个参数的谓词函数。Google测试目前支持最多5个谓词的断言。

治具

可以将共享公共对象或子例程的Google测试分组为固定装置。这是通用夹具的外观:

 

当用于夹具时,TEST()应将宏替换为,TEST_F()以允许测试访问夹具的成员和功能:

 

配置

将Google Test添加到CMake项目中时,您可以选择以下几个选项

  • 下载源并将其复制到项目结构中。

    这可能是开始使用该框架的最快方法,但是不会与Google Test存储库自动同步,因此您必须注意使源保持最新。

    您可以在下面的示例项目中找到使用此方法的示例

  • 如果您正在使用git,则可以将gtest添加为项目的git子模块

  • 另一种选择是使用CMake作为构建配置步骤的一部分下载gtest

 

要了解有关Google Test的更多信息,请浏览框架存储库中的示例。另外,请查看“高级”选项,以获取其他其他Google Test功能的详细信息,例如值参数化测试类型参数化测试

升压测试

Boost单元测试框架(Boost.Test)Boost库的一部分。它是一个功能齐全且可扩展的框架,具有各种断言宏,XML输出和其他功能。Boost.Test本身缺乏模拟功能,但可以与独立的模拟框架(例如gmock)结合使用。

跳棋

对于大多数Boost.Test检查器,您可以设置严重性级别:

  • 如果检查失败,WARN会生成一条警告消息,但错误计数器不会增加,并且测试用例会继续。

  • 当检查失败时,CHECK报告错误并增加错误计数器,但测试用例仍在继续。

  • 当应该终止测试用例的执行时,REQUIRE用于报告致命错误(例如,检查是否成功创建了稍后使用的对象)。

 

这样,Boost检查器通常是BOOST_[level]_[checkname]采用一个或几个参数的格式的宏。基本的宏BOOST_WARNBOOST_CHECKBOOST_REQUIRE。他们使用表达式的一个参数来检查,例如:

 

其他检查器的一些示例如下:

一般比较

BOOST_[level]_EQUALBOOST_[level]_NEBOOST_[level]_GT

万一发生故障,这些宏不仅会给出测试失败的消息,还会显示期望值和实际值:

 

浮点比较BOOST_[level]_CLOSEBOOST_[level]_CLOSE_FRACTION/BOOST_[level]_SMALL
异常检查BOOST_[level]_THROWBOOST_[level]_NO_THROW/BOOST_[level]_EXCEPTION

套房

您可以使用和宏对将Boost测试组织到套件中。一个简单的测试套件如下所示:BOOST_AUTO_TEST_SUITE(suite_name)BOOST_AUTO_TEST_SUITE_END()

 

治具

要使用Boost编写灯具,您可以使用在灯具类声明之后编写的常规BOOST_AUTO_TEST_CASE宏,也可以使用特殊的BOOST_FIXTURE_TEST_CASE宏:

 

配置

您可以在框架的三种用法变体之间进行选择:仅标头,静态库或共享库。选择最合适的选项时,请记住,Boost.Test用作仅标头可能需要大量的编译时间。

在下面的示例项目中找到共享库用法变体示例(切换到Boost.Test选项卡)。

渔获2

Google和Boost的Catch2的主要区别在于它是仅标头的测试系统:要使用Catch2创建测试,只需下载并仅包含一个标头文件catch.hpp即可。框架的名称代表Headers(版本2)中的C ++自动测试用例。CLion支持1.7.2版和更高版本的Catch。

和Boost.Test一样,Catch2也没有提供模拟功能。但是,您可以将其与独立的模拟框架(例如HippomocksFakeItTrompeloeil)结合使用

样品测试

以下示例显示了一个用Catch2编写的简单测试:

在上面的示例中,Life, the universe and everything是自由格式的测试名称,该名称必须唯一。TEST_CASE宏的第二个参数是两个标签[42]和的组合[theAnswer]。测试名称和标签都是常规字符串,不限于有效的C ++标识符。您可以通过指定通配测试名称或标记表达式来运行测试集合。

注意断言行REQUIRE(theAnswer() == 42)。与其他框架不同,Catch2没有用于捕获各种条件形式的断言集合。相反,它解析条件表达式的实际C / C ++代码,并使用它来描述结果:

 

REQUIRE宏观中止失败的测试,而替代CHECK宏只报告了故障,并让在测试矣。在这两个宏中,您可以使用所有C ++比较运算符并以任何顺序传递参数。

栏目

Catch2的另一个重要功能是组织案例和部分中的测试的方式(同时还支持基于类的Fixture机制)。从文档中查看以下示例:

 

在以上代码段中,TEST_CASE从头开始执行每个SECTION。强制执行REQUIRE顶部的两个语句为5,并且在每个节的入口处至少为5。这样,共享库就可以在堆栈上分配,而无需为它们创建一个夹具类。每次运行时,Catch2都会执行一个部分,而跳过其他部分。下次,它将执行第二部分,依此类推。TEST_CASEsizecapacityTEST_CASE

可以嵌套各节以创建一系列检查操作。每个叶节(内部没有嵌套节的节)执行一次。当父节失败时,它将阻止子节运行。例如:

 

Catch2还支持用于测试用例和部分的替代BDD样式语法

模板测试

Catch2以以下宏的形式支持类型参数化的测试用例:

  • TEMPLATE_TEST_CASE( test name , tags, type1, type2, ..., typen )
  • TEMPLATE_PRODUCT_TEST_CASE( test name , tags, (template-type1, template-type2, ..., template-typen), (template-arg1, template-arg2, ..., template-argm) )
  • TEMPLATE_LIST_TEST_CASE( test name, tags, type list )

这些宏的行为与normal相同TEST_CASE,但是会针对每种类型或类型组合运行。有关详细信息,请参见类型参数化的测试用例

除了类型参数化的测试用例之外,Catch2还提供TEMPLATE_TEST_CASE_SIGTEMPLATE_PRODUCT_TEST_CASE_SIG用于创建基于签名的参数化测试用例,它们具有与其他签名参数相似的语法:

有关更多信息,请参阅基于签名的参数化测试用例

配置

要开始使用Catch2,请使用文档中的链接下载catch.hpp标头,并将其复制到您的项目树中。请参阅下面的示例项目中的示例(切换到Catch2选项卡)。

文档测试

与Catch2相似,Doctest是具有自注册测试的单头框架。如其文档所述,Doctest是在Catch之后设计的,并共享Catch代码的某些部分-请参阅doctest与Catch有何不同

Doctest不支持模拟但是您可以将其与第三方模拟库(例如trompeloeilgooglemockFakeIt)集成

样品测试

用Doctest编写的简单测试如下所示:

在上面的示例中,测试阶乘函数是自由格式的测试名称,不必唯一。CHECK是Doctest的断言宏之一

 

测试用例,子测试和套件

Doctest提供了使用子案例的机制来创建具有共享的公共设置和拆卸的嵌套测试(但是,还支持基于类的夹具)。

在上面的代码中,TEST_CASE()从头开始执行每个SUBCASE()。每个子案例的入口处有两个REQUIRE语句保证该size值为5且capacity至少为5。如果CHECK()-s之一失败,则表示测试失败,但执行继续。在每次运行时TEST_CASE(),Doctest都会执行一个子案例,并跳过其他子案例。下次,执行第二个,依此类推。

与Catch的部分相似,子案例可以嵌套以创建检查操作序列。每个叶子子案例(内部没有嵌套子案例的子案例)执行一次。当父子案例失败时,它将阻止子子案例运行。

您可以使用TEST_SUITE()TEST_SUITE_BEGIN()/TEST_SUITE_END()宏将测试用例分组到套件中:

Doctest还支持BDD样式的语法

配置

要开始使用Doctest,请下载最新版本的doctest.h并将其复制到您的项目树中。请参阅下面的示例项目中的示例(切换到Doctest选项卡)。

CLion中的单元测试

CLion对Google Test,Boost.Test,Catch2和Doctest的集成包括对框架库的完整代码洞察力,专用的运行/调试配置,用于运行或调试测试/套件/固定装置并检查其状态的装订线图标,专门的测试运行程序以及还为测试和灯具类生成代码(适用于Google Tests)。

 

对于CMake项目,CLion还支持CTest

为您的项目建立测试框架

在本章中,我们将讨论如何在CLion中的项目中添加Google Test,Boost.Test,Catch2和Doctest框架,以及如何编写一组简单的测试。

例如,我们将使用您可以从github repo克隆的DateConverter项目。该程序计算公历日期格式的日期的绝对值,并将其转换为儒略历日期。最初,该项目不包含任何测试-我们将逐步添加它们。要查看框架之间的差异,我们将使用全部四个框架来执行相同的测试。您可以在DateConverter_withTests存储库中找到项目的最终版本。以下是项目结构的转换方式:

带有和不带有测试的示例项目

 

对于每个框架,我们将执行以下操作:

  1. 将框架添加到DateConverter项目。

  2. 创建两个测试文件AbsoluteDateTest.cppConverterTests.cpp。这些文件将为每个框架类似地命名,并且它们将包含使用特定框架的语法编写的测试代码。

 

现在,让我们打开克隆的DateConverter项目,并按照以下选项卡中的说明进行操作:

 
谷歌测试
升压测试
渔获2
文档测试
 

包括Google测试框架

  1. DateConverter项目根目录下为Google Tests创建一个文件夹。在其中,为框架的文件创建另一个文件夹。在我们的示例中,分别是Google_testsGoogle_tests / lib文件夹。

  2. 从官方存储库下载Google Test 。将googletest-master文件夹的内容提取到Google_tests / lib中

  3. CMakeLists.txt文件添加到Google_tests文件夹(在项目树中右键单击它,然后选择New | CMakeLists.txt)。添加以下行:

  4. CMakeLists.txt 脚本中,在最后添加一行,然后重新加载该项目。add_subdirectory(Google_tests)

添加Google测试

  1. 单击项目树中的Google_tests文件夹,然后选择“新建” |“新建”。C / C ++源文件,将其称为AbsoluteDateTest.cpp

    CLion提示将此文件添加到现有目标。我们不需要这样做,因为下一步将为此文件创建一个新目标。

    ConverterTests.cpp重复此步骤。

  2. 添加两个源文件后,我们可以为它们创建一个测试目标并将其链接到DateConverter_lib库。

    将以下行添加到Google_tests / CMakeLists.txt

  3. 将我们的支票的Google测试版本从AbsoluteDateTest.cppConverterTests.cpp复制到您的AbsoluteDateTest.cppConverterTests.cpp文件。

    现在可以开始运行测试了。例如,我们的点击在旁边的左侧排水沟DateConverterFixture中声明ConverterTests.cpp并选择运行...。我们将得到以下结果:

    Google测试结果

运行/调试测试配置

测试框架提供了main()测试程序的条目,因此可以在CLion中将它们作为常规应用程序运行。但是,我们建议对Google TestBoost.TestCatch2Doctest使用专用的运行/调试配置。这些配置包括与测试相关的设置,并使您能够从内置的测试运行器中受益(如果您将测试作为常规应用程序运行则无法使用)。

创建测试的运行/调试配置

  • 前往运行| 编辑配置,单击并选择特定于框架的模板之一:

    运行/调试测试框架的配置模板

     

 

CLion原子地为与gtestgmock链接的Cmake目标创建Google Test配置,以及为检测到的Doctest目标创建Doctest配置。

设置您的配置

  • 根据框架,指定测试模式suite标签(对于Catch2)。字段中提供了自动完成功能,可帮助您快速填写它们:

    配置字段中的自动完成

     

  • 指定测试模式时,可以使用通配符。例如,将以下模式设置为仅运行示例项目中的PlusOneDiffPlusFour_Leap测试:

    在测试模式中使用通配符

     

  • 在配置设置的其他字段中,您可以设置环境变量或命令行选项。例如,在“程序参数”字段中,您可以设置-sCatch2测试以强制通过测试以显示完整输出,或--gtest_repeat多次运行Google测试:

    程序参数中的标志

     

    输出将如下所示:

     

 

您可以修改模板本身,而不是分别编辑每个配置:在默认情况下,设置将在基于该模板的新配置中使用。

测试的装订线图标

在CLion中,有几种方法可以启动测试的运行/调试会话,其中一种方法是使用特殊的装订线图标。这些图标有助于快速运行或调试单个测试或整个套件/夹具:

测试的装订线图标

 

装订线图标还显示测试结果(如果可用):成功或失败。

当使用装订线图标运行测试/套件/装置时,CLion将创建相应类型的临时运行/调试配置。您可以在列表中看到这些配置为灰色。要保存临时配置,请在“编辑配置”对话框中将其选中,然后按:

保存临时测试配置

 

测试跑步者

运行测试配置时,结果(和过程)将显示在“测试运行器”窗口中,该窗口包括:

  • 进度条,显示到目前为止已执行的测试百分比,

  • 所有正在运行的测试及其状态和持续时间的树状视图

  • 测试的输出流,

  • 工具栏,其中包含重新运行失败的测试,导出或打开自动保存的先前结果的选项,按字母顺序对测试进行排序以轻松找到特定测试,或按持续时间对它们进行排序以了解哪个测试比其他测试更长。

 

例如,如果我们有意破坏示例项目中的某些Catch2测试,则这是测试运行器窗口的外观:

测试跑步者

 

Google测试的代码生成

如果您使用的是Google Test框架,CLion的Generate菜单可以帮助您节省编写测试代码的时间。在包含的测试文件中gtest,按Alt+Insert以查看代码生成选项。

从灯具调用时,菜单还包括SetUp方法TearDown方法

生成测试菜单

 

对于夹具测试,代码生成转换TEST()宏插入相应的TEST_F()TEST_P()TYPED_TEST(),或TYPED_TEST_P()(见类型化试验)。

 

其他特性

测试宏的快速文档

为了帮助您探索测试框架提供的宏,“快速文档”弹出窗口显示了最终的宏替换并正确设置了其格式。它还突出显示了结果替换中使用的字符串和关键字:

快速文档弹出窗口中的格式化宏扩展

 

显示测试清单

为了减少初始索引编制的时间,CLion使用了惰性测试检测。这意味着在打开某些测试文件或运行/调试测试配置之前,测试将从索引中排除。要检查当前为您的项目检测到的测试,请从“帮助” |“显示测试列表”中调用 查找动作。请注意,调用此操作不会触发索引编制。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值