(图片: josh@unsplash,字数: 1500,时间: 2分钟)
交叉领域是容易产生新思想和新技术的地方。软件测试和代码评审(code review)是软件质量保障体系的两大重要组成部分。看似互不相关的它们,如果结合在一起,会擦出什么样的火花?
今天,我们来探讨下软件测试和代码评审的一个交集,即"测试驱动代码评审"。在敏捷模式中,如果说测试驱动开发(TDD)是利用测试来指引和改进软件开发工作的实践,那么测试驱动代码评审(TDCR)就是利用测试来指引和改进代码评审工作的实践。在我看来,测试驱动代码评审的具体表现形式,可以归纳为以下三种。
一,测试通过之后,才开始代码评审。
这一点比较容易理解。在持续集成中,开发者每次提交代码都会触发一系列自动化检查,分析和测试任务,以检验代码改动是否能够合入主干。代码评审应该在这些自动化任务成功之后才开始。
毕竟,代码评审是人工的,需要消耗宝贵的(资深)开发者时间。自动化任务与人工代码评审基本符合二八定律。那就是说,先由自动化检查/分析/测试任务发现(过滤)80%的代码问题,然后才启动代码评审流程,目的是发现自动化任务所不能发现的剩余20%代码问题。
这里有一个副作用:收到评审意见往往意味着要提交新的代码改动,从而增加自动化任务执行次数,加重持续集成负荷。然而,权衡利弊,这样做是值得的,毕竟代码评审准入门槛变高了,代码评审效率也随之提高。
二,在代码评审时,先阅读测试代码,后阅读产品代码。
对于一个陌生事物,太早陷入细节会让人迷失方向。明智的做法是,先观察和熟悉事物的整体面貌,然后再决定是否以及如何深入它的细节。在代码评审时,先阅读测试代码,可以帮助我们更好把握代码改动的全貌。怎么理解这一观点呢?
首先,代码改动是为满足某个需求而提交的,而与需求直接挂钩就是测试用例。通过阅读测试代码,我们可以了解软件功能是否精准覆盖了用户需求,是否遗漏了某部分需求?是否存在过度实现,即实现了用户(当前)并不需要的需求?
其次,通过阅读测试代码,我们可以了解软件设计的好坏。测试用例可以看作为软件外部接口的用户。如果用例在调用某个接口时,需要传入大量参数,那么接口封装以及软件职责分配做得不好;如果用例在测试某个方法时,需要Mock许多外部对象,那么软件的低耦合设计做得不好。
另外,通过阅读测试代码,我们还可以了解开发者实践TDD的情况。如果测试代码严重依赖产品代码实现细节,那么很有可能开发者是先写产品代码后补测试用例的,从而违反了TDD原则。这种情况下,我们在评审产品代码时,需要重点关注代码是否存在未被测试覆盖的隐含特性。
大家可以看到,不深入产品代码细节,仅仅通过阅读测试代码,我们就可以发现一些软件设计,软件功能,开发习惯等方面存在的问题。在建立了这些初步认识之后,我们再去阅读产品代码,会更容易理解代码细节,评审也更高效。
三,重点评审未被测试覆盖的产品代码。
代码覆盖率是度量测试完整性的一个重要指标。虽然软件研发团队纷纷引入代码覆盖率度量技术,但是据观察,覆盖率报告经常被晾在一边,很少有人真正关注它。然而,"树挪死,人挪活"。将覆盖率报告换个位置,把它集成到代码评审流程中,能够更好发挥作用。
下图是Google的代码评审页面。大家可以发现,在阅读代码改动的同时,我们可以同步阅读代码改动的覆盖率情况(图中左侧行号颜色,绿色代表已覆盖,橙色代表未覆盖),从而直观了解哪些代码行没有被测试覆盖。虽然被测试覆盖的行不一定完美,但是未被测试覆盖的行一定是值得重点评审的。
以上就是测试驱动代码评审的三种典型形态。总的来说,借助软件测试的力量,代码评审可以更有针对性,效率也更高。个人相信,作为软件工程新实践,测试驱动代码评审会逐渐得到大家的重视。