TDD(测试驱动开发)测试框架

C/C++怎么做好单元测试

2019-03-21  本文已影响15人  MagicBowen

什么是单元测试

单元测试是软件开发过程中的一种质量保证手段。最初的来源是想模仿对硬件芯片做单元测试那样,在软件中也能对小的软件单元进行测试,从而保证软件中某个局部设计的正确性。

传统的单元测试定义

传统软件单元测试将被测单元的粒度规定为软件中最小的功能模块。对于C语言通常指一个函数,对于Java或者C++语言通常指一个类。

传统做法是针对被测单元的实现细节进行各种白盒测试,即针对被测代码的实现逻辑进行各种分支测试和覆盖测试。

传统的单元测试由于缺乏自动化工具的支持,往往在测试中通过打印输出测试结果,由人工比对每次测试是否成功。

现代单元测试定义

随着技术的进步和人们对软件单元测试方法的发展,现代单元测试的定义已经发生了很大的变化。

总结一下:我们认为现代化的单元测试的定义应该是:一种满足一键式全自动化运行的软件单元级别的黑盒测试。

单元测试的价值

我们认为遵循现代单元测试最佳实践的单元测试过程,可以为软件团队带来如下价值:

单元测试要求

我们认为合格的单元测试应该满足以下要求:

单元测试工具

随着技术的成熟,单元测试工具现在已经变得很容易获得和使用了。自从Kent Beck(敏捷软件开发方法泰斗,极限编程和测试驱动开发的提出者)为Java语言开发并开源了JUnit框架后,一下子将单元测试带到了一个新的境地。随后其它语言纷纷效仿JUnit推出了自己的开源单元测试框架。人们后来对所有编程语言的这一系列框架起了个统一的名称,叫做xUnit测试框架

评判xUnit测试框架的标准

目前对于任一编程语言,都能找到好几款开源的的xUnit测试框架,那么如何对比并选择合适好用的xUnit框架呢?一般从如下几个维度去评估。

主流C/C++ xUnit测试框架对比

根据上面提到的判断维度,我们分析对比一下当前主流的C/C++ xUnit测试框架。

测试框架特性 Boost Test CppUnit Gtest TestNgpp
是否开源
自动检测注册
断言能力 较弱
支持Fixture 支持 支持 支持 支持
支持Suite分组 支持 支持 支持 支持
支持用例过滤 支持 支持 支持 支持
测试报表 不支持 支持 支持 支持
测试能力
用例依赖管理 不支持 不支持 不支持 支持
沙盒模式 不支持 不支持 不支持 支持
社区使用程度 一般 使用程度很高 一般

通过上面的分析可以看到,主流的C++ xUnit测试框架都是开源的。其中TestNgpp功能虽然最强大,但是用户较少。Google推出的Gtest框架使用范围最广,社区支持程度也最好,从功能上来说简单易用,作为上手框架最为合适。其它框架由于各种缺陷不建议再选用了。

Mock框架推荐

在做单元测试的时候避免不了要为被测代码打桩,而mock框架主要是为了简化打桩过程。使用mock框架可以让打桩代码非常容易撰写,而且不会侵入实现代码。比如两个测试用例需要同一个桩函数:函数声明相同但是返回值不同。在没有mock框架的情况下解决这类问题非常麻烦,而mock框架则可以轻而易举的应对此类问题。

Mock框架除了提供打桩的功能外,还提供其它更加强大的功能。例如何以监听用户对打桩代码的调用行为,并监控这些行为是否符合预期。

对于Java语言来说,可用的mock框架五花八门,选择范围非常广。但是对于C++语言来说,只有两款易用的mock框架:gmock和mockcpp。这两款都是开源软件,经过使用对比,mockcpp功能强大且用户体验胜过gmock,所以基本没有什么好对比和推荐的,如果需要直接上mockcpp就好了。

单元测试过程

基于前面介绍的xUnit测试框架,为代码做单元测试的过程一般分为如下主要步骤:

单元测试环境搭建

这一步是在每个开发人员的机器上搭建单元测试环境。需要做的步骤如下:

测试编写过程

当开发人员的机器上已经搭建好单元测试工具后。接下来就可以对代码进行单元测试了。

一般使用xUnit框架进行单元测试主要有以下几个过程:

通过持续集成进行部署

一般一个大型的软件团队都是多人合作开发的模式,这时会通过公共的代码管理仓库进行协调。项了保证代码每次修改的安全性,需要搭建持续集成服务器。持续集成服务器就是安装了持续集成软件(例如开源的Jenkins软件)的机器。该机器会实时监控代码管理仓库,一旦发现有新的代码提交,就会触发一系列用户定义的持续集成任务(参加下面的示意图)。

以Jenkins举例来说,常见的可配置的持续集成任务包括:

由于持续集成服务器时刻监控代码管理仓库,一旦有新的代码合入就立即执行对应的任务:例如编译、构建、执行所有单元测试用例等等。持续集成工具都支持结果通知的配置,当存在某项任务失败则通过看板或者邮件的方式通知指定负责人,这样一旦有人提交的代码造成编译构建失败或者单元测试失败,就会立即被发现。这样就避免了低质量的软件合入到代码仓库后,到很晚才能知道的问题。

测试覆盖率统计

和单元测试相关性较大的一个是测试覆盖率报表的生成。对于C/C++,可选的测试覆盖率工具并不多,见下表。

工具 平台 是否开源
Coverage Validator windows 商用
OpenCppCoverage windows 开源 (只支持VS2013以上版本)
gcov + lcov linux 开源

测试覆盖率工具一般安装部署在持续集成对应的机器上,这样每次持续集成服务器跑完测试用例后,就会根据当前的测试运行情况自动计算出所有代码的测试覆盖率结果,可以详细看到每一行代码的的覆盖情况。生成的报表可以自动发布成一个网页,项目中的所有人都可以看到。

单元测试注意事项

前面我们介绍了单元测试的工具和实施过程,接下来我们看看做好单元测试要注意的一些事项。

常见误区

在实践的过程中,发现经常有团队虽然开发了大量的单元测试,但是单元测试有效性却很低,付出了大量成本却并没有得到单元测试的收益。总结之后主要有以下一些原因:

如何降低单元测试成本

从长期来看,降低单元测试的成本并不在于使用了更好的单元测试工具,而在于降低由于被测代码改动导致单元测试随之变动的频度。软件之所以和硬件不同就在于它的软,它的存在价值就是为了应对变化。而软件的变化性往往越向内传递越剧烈,这导致了软件单元级别的设计经常处于变更的核心旋涡之中。所以经常见到有些软件团队,一旦需求变化快工期紧,很快就把单元测试抛弃掉了。被测代码变化导致单元测试跟着变化不可能不发生,但是我们要通过设计降低这种联动变化的概率,这样才能降低单元测试的维护成本。

要让单元测试能够以较低成本维护,需要注意一下事项:

由上可见,做好单元测试不只是掌握单元测试工具的使用就万事大吉了。需要对开发人员的能力进行提升,主要包括:

将单元测试与整体测试策略结合

单元测试只是软件测试策略中的一个环节,其它的还有系统测试,集成测试,组件测试等。每一级的测试都有其价值和不足,所以整体测试策略需要关注如何把这些测试策略整合起来,让整体的成本收益率最好。所以从根本上需要站在全局规划整体的测试策略,这块可以参考敏捷测试的测试象限和金字塔模型理论,然后根据项目的实际情况制定合理的整体测试策略。

相关推荐材料和培训

  1. 最全的xUnit开源测试框架列表,针对每种编程语言:https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
  2. xUnit测试最佳实践:《xUnit Test Patterns》可是说是最权威的一本书。
  3. 测试策略规划:《敏捷软件测试》,测试象限和测试金字塔理论。
  4. 《TDD in Embeded C》:嵌入式环境的TDD实践指导。
上一篇 下一篇

猜你喜欢

热点阅读