所有代码都需要单元测试覆盖吗?

   单元 测试(unit  testing)已经越来越得到广大开发者的认可。作为低成本、速度快、稳定度高的 自动化测试手段, 单元测试可以在类和函数级别对代码进行质量守护,有助于避免尴尬、耗时的错误。当然,相比 功能测试(Functional testing)和端到端测试(end-to-end testing),单元测试能够寄予的产品级别的信心要略低一些,因而各个粒度的测试应该是相辅相成的,互为补充。
  常常听到一些组织要求开发团队提高单元测试覆盖率,换来的却是怨声载道,或者是一堆应付差事的垃圾测试(没有断言的测试,都见过吧)。尽管,低测试覆盖率意味着对质量的信心不足,但是,单元测试覆盖率真的要达到100%才好吗?
  100%听起来肯定比95%要好,但是区别在于那些额外测试的价值对你可能是微不足道的。这要看哪种代码没有被测试覆盖,以及你的测试能否暴露程序的错误。100%的覆盖率并不能够确保没有缺陷——它只能保证你所有的代码都执行了,而不管程序的行为是否满足要求。与其追求代码覆盖率,不如将重点关注在确保写出有意义的测试。
  测试越多,额外测试的价值越少。第一个测试最有可能是针对代码最重要的区域,因此带来高价值与高风险。当我们为几乎所有事情编写测试后,那些仍然没有测试覆盖的地方很可能是最不重要和最不可能破坏的。
  编写一些测试是不费脑筋的,但随着我们接近完全的代码覆盖率,我们不那么确定了——我们差不多已经为一切都编写了测试,而剩下的没有测试的代码是微不足道,几乎不会破坏。这就是所谓的收益递减。要想从单元测试中获得更多的收益,需要重新将单元测试从质量工具定位成设计工具。TDD等方式可以帮助我们做到这一点。
  换句话说,向代码基增加100个精挑细选的自动化测试是明显的改善,但当我们已有30000个测试时,这些额外的100个测试就无足轻重了。
  比赛场上的赢家是那些将注意力集中在赛场上的选手,而不是紧盯着计分板的人。—巴菲特
  总之,当你觉得生产环境中报来的bug很少了,或者你能够自信地对代码随时进行修改,单元测试就已经足够多了。
  另一方面,也有些观点认为,不但不值得追求高覆盖率,甚至写单元测试本身就是非常耗时和难以维护的重复 工作。这种极端观点我同样不赞同。
  代码覆盖率不能告诉我们代码质量的高低,也不能用了评判开发人员的绩效。但它能够告诉我们哪些代码还没有被测试覆盖,哪里有漏网之鱼。至于这鱼值不值得抓,还是取决于开发人员的经验进行风险判断。那么,接下来我们再来分析一下,到底哪些鱼值得抓。


   unit-test-quadrants
  图中,产品代码可以分为四个类别,纵轴是从单元测试中得到的收益,横轴是单元测试的成本,我们从投入产出的角度来分析,到底哪些代码适合于进行单元测试:
  琐碎且无甚依赖的代码。比如getter/setter, 比如简单地调用系统时间,比如 toString()等等,基本是不需要测试的。虽然测起来容易,但我们有信心说它们出错的概率也非常低,测这种代码的确索然无味。
  承上启下的代码。比如用MVC框架实现的代码里,某些service层只是简单地被Action层调用,然后转发到下一层去。这种粘合代码不具备太多被测试的价值,而且由于是衔接上下两层的传话筒,测起来却需要对周围各层进行mock或打桩。要想验证其所做的那一点点工作,其实还挺麻烦的。当然,也有一些观点比如”London School TDD”坚持认为,对于企业级应用,就要像制作香肠一样,一层一层地对交互(interaction-based)进行测试,每测一层都需要mock的帮助。
  具有算法和业务逻辑的代码。比如排序或处理数据等代码,这些是最值得进行单元测试的代码了。虽然有一定的成本,但是由于算法逻辑的输入输出非常确定,结构复杂且具有业务价值,外部依赖较少,在这上面投入是一定可以得到丰厚回报的。这即是”Classic TDD”观点,对状态进行测试(state-based)。
  过于复杂的代码。充满复杂逻辑、交织在一起、散发着各种坏味道的遗留代码,就像一座未开发的金矿,你需要做很多清理工作,才能顺利地进行开采,否则将寸步难行,不知从何下手,而且成本极高。这种代码建议先进行粗粒度重构和接口测试(孰先孰后要根据实际情况),破除掉严重的依赖,找到所谓的接缝(Seam),将具有逻辑的部分与承上启下的代码分离开,然后立即将单元测试注入到接缝中,形成对有逻辑代码的保护网,随后继续缩小重构的范围,不断地添加测试和重构,逐渐渗透形成一张网,将又臭又硬的代码块切碎。
  除了3)以外的代码怎么测?可以采用其他测试手段,比如功能性测试来进行粗粒度覆盖,同样能提升对产品的信心,并且成本更低,特别适合敏捷迭代式开发,能够在有限的时间盒内达到预期的质量水平。
  此外,持续地重构代码,同时编写更有效的单元测试,也是敏捷开发者应该具备的基本功,有助于提高投入产出比。
最新内容请见作者的GitHub页:http://qaseven.github.io/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单元测试覆盖率是衡量软件项目中单元测试对源代码覆盖程度的度量指标。它表示在单元测试中执行的代码与整个代码库中的可执行代码之间的比例。单元测试覆盖率旨在评估测试用例是否足够全面地覆盖代码的不同部分,以便发现潜在的错误和问题。 单元测试覆盖率通常以百分比形式表示,可以分为几个不同的度量指标: 1. 语句覆盖率(Statement Coverage):表示在单元测试中执行的代码语句与总代码语句数之间的比例。它衡量了每个语句是否被至少执行一次。 2. 分支覆盖率(Branch Coverage):表示在单元测试中执行的分支与总分支数之间的比例。它关注代码中的条件语句和分支,确保每个分支都被至少执行一次。 3. 条件覆盖率(Condition Coverage):表示在单元测试中执行的条件(例如,布尔表达式)与总条件数之间的比例。它关注每个条件的真假值,并确保每个条件的所有可能情况都被覆盖到。 4. 路径覆盖率(Path Coverage):表示在单元测试中执行的代码路径与总代码路径数之间的比例。它关注代码中的不同路径和执行流程,确保所有可能的执行路径都被覆盖到。 通过衡量单元测试覆盖率,开发团队可以评估测试用例的质量和完整性。较高的覆盖率通常意味着测试用例对代码进行了更全面的测试,从而提高了软件的质量和稳定性。然而,单元测试覆盖率并不是唯一衡量测试质量的指标,还需要考虑测试用例的有效性和边界条件的覆盖等方面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值