C++ 单元测试工具:探索 Catch 的强大功能

更新:11-22 民间故事 我要投稿 纠错 投诉

大家好,今天给各位分享C++ 单元测试工具:探索 Catch 的强大功能的一些知识,其中也会对进行解释,文章篇幅可能偏长,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在就马上开始吧!

那么我们应该使用哪一个呢?如果你在Google 中搜索:最好的C++ 单元测试框架。前两个是,一个是stackoverflow 问答,另一个是reddit 问答。这两个问题都指向同一个单元测试框架:Catch。

为什么使用 Catch

Catch的官方文档中有一篇文章:为什么我们还需要另一个C++测试框架?如果您有兴趣,可以看一下。对我来说,它最吸引人的地方是:

几乎不需要任何配置。它是一个单头文件测试框架。它根本不需要任何东西。

C++ 的单元测试工具 —— Catch

发表于2016-08-22|阅读次数: 2356

如果你平时使用Java语言进行开发,当你听到单元测试工具这个词时,你可能会立即想到JUnit。作为一名C++ 软件工程师,当我第一次计划对我的程序进行单元测试时,我的第一个想法是:有这样的工具吗?搜索了一段时间后,我的回答是:我应该使用哪一个?

我在学校的时候很少听说过C++单元测试工具,所以我一直以为这样的工具不存在。后来慢慢发现我们的选择比你想象的要多:Catch、Boost.Test、UnitTest++、lest、bandit、igloo、xUnit++、CppTest、CppUnit、CxxTest、cpputest、googletest、cute。

那么我们应该使用哪一个呢?如果你在Google 中搜索:最好的C++ 单元测试框架。前两个是,一个是stackoverflow 问答,另一个是reddit 问答。这两个问题都指向同一个单元测试框架:Catch。

为什么使用 Catch

Catch的官方文档中有一篇文章:为什么我们还需要另一个C++测试框架?如果您有兴趣,可以看一下。对我来说,它最吸引人的地方是:

几乎不需要任何配置。它是一个单头文件测试框架。它根本不需要任何额外的配置。语法非常简单明了。用它编写的测试代码就像自然语言一样容易理解。

如何使用它

Catch 是一个单一头文件库,您只需#include“catch.hpp”即可。然后你可以像这样编写测试代码:

|

SCENARIO( "矢量可以调整大小和调整大小", "[矢量]" ) {

GIVEN( "带有一些项目的向量" ) {

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

WHEN( "尺寸增加" ) {

v.调整大小(10);

THEN( "大小和容量变化" ) {

要求( v.size()==10 );

要求( v.capacity()=10 );

}

}

WHEN( "尺寸减小" ) {

v.调整大小(0);

THEN( "大小改变但容量不变" ) {

要求( v.size()==0 );

要求( v.capacity()=5 );

}

}

WHEN( "保留更多容量" ) {

v. 保留(10);

THEN( "容量改变但大小不变" ) {

要求( v.size()==5 );

要求( v.capacity()=10 );

}

}

WHEN( "保留较少容量" ) {

v.reserve(0);

THEN( "大小和容量都没有改变" ) {

要求( v.size()==5 );

要求( v.capacity()=5 );

}

}

}

}

|

这是几乎可读的代码,不需要解释。这种测试方法称为BDD(行为驱动开发)。这是最新的测试方法。它强调“行为”而不是“测试”。如果您有兴趣,可以阅读这篇文章。

如果你习惯了传统的TDD测试,你可以这样编写测试代码:

|

TEST_CASE( "向量可以调整大小和调整大小", "[向量]" ) {

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

SECTION( "调整大小以改变大小和容量" ) {

v.调整大小(10);

要求( v.size()==10 );

要求( v.capacity()=10 );

}

SECTION( "调整较小的大小会更改大小,但不会更改容量" ) {

v.调整大小(0);

要求( v.size()==0 );

要求( v.capacity()=5 );

}

SECTION( "保留更大的容量会改变容量,但不会改变大小" ) {

v. 保留(10);

要求( v.size()==5 );

要求( v.capacity()=10 );

}

SECTION( "保留较小的值不会改变大小或容量" ) {

v.reserve(0);

要求( v.size()==5 );

要求( v.capacity()=5 );

}

}

|

其实这两种方法是等价的。 SCENARIO只是TEST_CASE的别名,GIVEN、WHEN和THEN最终映射到SECTION。区别仅在于测试方式。您可以根据自己的喜好使用自己喜欢的方法。

SECTION 的执行顺序

上面的代码非常清晰易懂,但是有一点需要注意,那就是SECTION的执行方法。上一节的代码中,TEST_CASE中有4个SECTION,它们并不是纯粹的顺序执行关系。第一个SECTION执行完成后,会重新开始执行,并跳过已经执行过的SECTION。也就是说,上面代码的执行路径大致是这样的(去掉SECTION宏后):

|

//第1 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.调整大小(10);

要求( v.size()==10 );

要求( v.capacity()=10 );

//第2 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.调整大小(0);

要求( v.size()==0 );

要求( v.capacity()=5 );

//第3 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v. 保留(10);

要求( v.size()==5 );

要求( v.capacity()=10 );

//第4 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.reserve(0);

要求( v.size()==5 );

要求( v.capacity()=5 );

|

测试代码的执行入口

在C++中,任何需要执行的代码都必须经过main函数,测试代码也不例外。 Catch不需要我们自己编写main函数来调用这些测试代码。它提供了默认的main函数入口。您只需在(并且仅在)一个文件中添加以下配置宏:

|

#定义CATCH_CONFIG_MAIN

include "catch.hpp"

|

最佳实践

命令行参数

最佳做法是使用单独的文件来放置这两行代码,并将测试代码写入其他文件中。

原因是Catch是一个单一头文件库,这意味着它的内容最终会出现在所有包含这个头文件的编译单元中。如果我们把测试代码和上面两行代码放在一起,每次编译测试代码的时候都需要编译Catch内核,这样会导致编译速度非常非常慢。如果将两者分开的话,Catch的内核只需要在一个文件中编译一次(因为Catch内部已经做了判断,如果内核已经编译过了,就不需要再编译一次,即使你使用#在多个文件中包含“catch”.hpp”)。该文件的编译速度比较慢,但该文件不会改变,所以整个开发周期只需要编译一次,并且不断更新测试编译速度代码会快得多。

TAG

Catch提供的主函数实现的另一个强大功能是丰富的命令行参数。你可以选择执行部分TEST_CASE,也可以选择不执行部分TEST_CASE。您可以使用它来调整输出。 xml文件中,还可以用它从文件中读取需要测试的测试用例。这些命令的具体使用请参考Catch官方文档的命令行部分。

提供自己的 main 函数入口

需要注意的是,这些强大的命令行大多数都是基于TAG 的,它是TEST_CASE 定义中的第二个参数。

|

TEST_CASE( "向量可以调整大小和调整大小", "[向量]" )

|

在上面的定义中,“[vector]”是一个TAG,您可以提供多个TAG:

|

TEST_CASE( "D", "[小部件][小工具]" ) { /* . */}

|

此时,您可以在命令行中根据TAG选择是否执行TEST_CASE。例如:

|

./catch "[vector]" //只执行那些标记为向量的测试用例

|

另外,还可以使用一些特殊字符,如[.]来表示隐藏。 [.integration]表示默认隐藏,但可以在命令行中使用[.integration] TAG执行。其他一些特殊字符请参考官方文档的测试用例和章节部分。

|

./catch //默认不执行集成

./catch "[.integration]" //使用TAG进行积分

|

其他内容

如果不喜欢上面的处理方式,想自己提供main函数,可以使用CATCH_CONFIG_RUNNER。具体细节请查看官方文档中的Supplying main() myself部分。

如何使用它

其实Catch本身比较简单,不需要太多其他的学习。它的大部分用法都非常直观。看完它的官方教程,基本上就可以入门了,然后有时间慢慢看。阅读其官方文档集

提供额外配置

语法非常简单明了,用它编写的测试代码就像自然语言一样易于理解。

SECTION 的执行顺序

Catch 是一个单一头文件库,您只需#include“catch.hpp”即可。然后你可以像这样编写测试代码:

|

SCENARIO( "矢量可以调整大小和调整大小", "[矢量]" ) {

GIVEN( "带有一些项目的向量" ) {

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

WHEN( "尺寸增加" ) {

v.调整大小(10);

THEN( "大小和容量变化" ) {

要求( v.size()==10 );

要求( v.capacity()=10 );

}

}

WHEN( "尺寸减小" ) {

v.调整大小(0);

THEN( "大小改变但容量不变" ) {

要求( v.size()==0 );

要求( v.capacity()=5 );

}

}

WHEN( "保留更多容量" ) {

v. 保留(10);

THEN( "容量改变但大小不变" ) {

要求( v.size()==5 );

要求( v.capacity()=10 );

}

}

WHEN( "保留较少容量" ) {

v.reserve(0);

THEN( "大小和容量都没有改变" ) {

要求( v.size()==5 );

要求( v.capacity()=5 );

}

}

}

}

|

这是几乎可读的代码,不需要解释。这种测试方法称为BDD(行为驱动开发)。这是最新的测试方法。它强调“行为”而不是“测试”。如果您有兴趣,可以阅读这篇文章。

如果你习惯了传统的TDD测试,你可以这样编写测试代码:

|

TEST_CASE( "向量可以调整大小和调整大小", "[向量]" ) {

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

SECTION( "调整大小以改变大小和容量" ) {

v.调整大小(10);

要求( v.size()==10 );

要求( v.capacity()=10 );

}

SECTION( "调整较小的大小会更改大小,但不会更改容量" ) {

v.调整大小(0);

要求( v.size()==0 );

要求( v.capacity()=5 );

}

SECTION( "保留更大的容量会改变容量,但不会改变大小" ) {

v. 保留(10);

要求( v.size()==5 );

要求( v.capacity()=10 );

}

SECTION( "保留较小的值不会改变大小或容量" ) {

v.reserve(0);

要求( v.size()==5 );

要求( v.capacity()=5 );

}

}

|

其实这两种方法是等价的。 SCENARIO只是TEST_CASE的别名,GIVEN、WHEN和THEN最终映射到SECTION。区别仅在于测试方式。您可以根据自己的喜好使用自己喜欢的方法。

测试代码的执行入口

上面的代码非常清晰易懂,但是有一点需要注意,那就是SECTION的执行方法。上一节的代码中,TEST_CASE中有4个SECTION,它们并不是纯粹的顺序执行关系。第一个SECTION执行完成后,会重新开始执行,并跳过已经执行过的SECTION。也就是说,上面代码的执行路径大致是这样的(去掉SECTION宏后):

|

//第1 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.调整大小(10);

要求( v.size()==10 );

要求( v.capacity()=10 );

//第2 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.调整大小(0);

要求( v.size()==0 );

要求( v.capacity()=5 );

//第3 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v. 保留(10);

要求( v.size()==5 );

要求( v.capacity()=10 );

//第4 节

std:向量v(5);

要求( v.size()==5 );

要求( v.capacity()=5 );

v.reserve(0);

要求( v.size()==5 );

要求( v.capacity()=5 );

|

include "catch.hpp"

在C++中,任何需要执行的代码都必须经过main函数,测试代码也不例外。 Catch不需要我们自己编写main函数来调用这些测试代码。它提供了默认的main函数入口。您只需在(并且仅在)一个文件中添加以下配置宏:

|

#定义CATCH_CONFIG_MAIN

最佳实践

|

命令行参数

TAG

最佳做法是使用单独的文件来放置这两行代码,并将测试代码写入其他文件中。

原因是Catch是一个单一头文件库,这意味着它的内容最终会出现在所有包含这个头文件的编译单元中。如果我们把测试代码和上面两行代码放在一起,每次编译测试代码的时候都需要编译Catch内核,这样会导致编译速度非常非常慢。如果将两者分开的话,Catch的内核只需要在一个文件中编译一次(因为Catch内部已经做了判断,如果内核已经编译过了,就不需要再编译一次,即使你使用#在多个文件中包含“catch”.hpp”)。该文件的编译速度比较慢,但该文件不会改变,所以整个开发周期只需要编译一次,并且不断更新测试编译速度代码会快得多。

010-1010 Catch提供的主函数实现的另一个强大功能是丰富的命令行参数。你可以选择执行部分TEST_CASE,也可以选择不执行部分TEST_CASE。您可以使用它来调整输出。 xml文件中,还可以用它从文件中读取需要测试的测试用例。这些命令的具体使用请参考Catch官方文档的命令行部分。

010-1010 需要注意的是,这些强大的命令行大多数都是基于TAG 的,它是TEST_CASE 定义中的第二个参数。

|

TEST_CASE( "向量可以调整大小和调整大小", "[向量]" )

|

在上面的定义中,“[vector]”是一个TAG,您可以提供多个TAG:

|

TEST_CASE( "D", "[小部件][小工具]" ) { /* . */}

|

此时,您可以在命令行中根据TAG选择是否执行TEST_CASE。例如:

|

./catch "[vector]" //只执行那些标记为向量的测试用例

|

另外,还可以使用一些特殊字符,如[.]来表示隐藏。 [.integration]表示默认隐藏,但可以在命令行中使用[.integration] TAG执行。其他一些特殊字符请参考官方文档的测试用例和章节部分。

|

./catch //默认不执行集成

./catch "[.integration]" //使用TAG进行积分

|

用户评论

浮殇年华

我一直在寻找一个功能强大、易于使用的 C++ 单元测试工具,看来 Catch 挺适合我的。

    有8位网友表示赞同!

繁华若梦

这个 Catch 工具听起来不错,想了解一下它的使用场景和学习难度如何?

    有13位网友表示赞同!

还未走i

对C++开发来说,单元测试真的很有必要,Catch 能帮助我们进行代码验证吗?

    有19位网友表示赞同!

爱你心口难开

之前用过一些别的 C++ 单元测试框架,希望 Catch 能更方便简洁。

    有20位网友表示赞同!

ゞ香草可樂ゞ草莓布丁

听说 Catch 支持断言非常完善,让我们更容易定位问题!

    有8位网友表示赞同!

巴黎盛开的樱花

学习一个新的工具总需要时间,不过如果像 Catch 这样好用,值得花一点功夫去尝试。

    有7位网友表示赞同!

无寒

对于 C++ 项目的开发,单元测试确实能提高代码质量,Catch 能提供很好的支持吗?

    有19位网友表示赞同!

一点一点把你清空

在网上看到过一些 Catch 的代码示例,感觉语法挺整洁的。

    有20位网友表示赞同!

肆忌

作为一个资深的 C++ 开发者,我一直在关注新的测试工具,这个 Catch 看起来很有潜力!

    有8位网友表示赞同!

微信名字

我想知道 Catch 支持哪些主流的编译器和 IDE 工具?

    有8位网友表示赞同!

空巷

我很想尝试一把 Catch 使用感受,希望能够找到一些详细的教程和文档。

    有17位网友表示赞同!

孤单*无名指

对新手来说,Catch 的学习成本有多高?是否有简便易懂的入门指南?

    有16位网友表示赞同!

沐晴つ

Catch 是否支持断点调试,这样能让我们更方便地分析测试结果吗?

    有15位网友表示赞同!

葵雨

这个 Catch 工具能够帮助我们快速生成测试用例吗?

    有5位网友表示赞同!

我就是这样一个人

我想知道 Catch 的社区是否活跃,有没有很多开发人员在使用和讨论它?

    有7位网友表示赞同!

将妓就计

Catch 支持跨平台的使用吗?这对于我来说非常重要。

    有16位网友表示赞同!

伪心

在实际项目中使用 Catch 的经验分享可以让我更好地了解它的功能和局限性吧?

    有5位网友表示赞同!

伤离别

如果我是 C++ 生涯初学者,推荐学习 Catch 作为第一套单元测试工具吗?

    有6位网友表示赞同!

【C++ 单元测试工具:探索 Catch 的强大功能】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:高效广告位资源运用策略:APP平台收益最大化指南 下一篇:探索梦境奥秘:揭开梦(上)的神秘面纱