TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

    软件研发,说一千道一万,最终还是要落地到写好代码做好测试上面,从软件行业长期实践来看,TDD是落实好以上两点的一个非常重要且适合规模推广的方法。

     TDD全称为Test Drive Design,也是大家普遍觉得非常难的实践,很多人甚至把TDD叫做To Difficult to Do,因为TDD不仅涉及软件研发方法论,还涉及系统本质复杂度的系统工程论的范畴,两者兼顾大家觉得非常难。不过TDD本身还是有清晰的步骤的,但真正开展TDD,还是有很多的湖水下的岩石要绕过去,这些岩石很像玻璃天花板,给团队完成巨大阻碍,但自己却很难意识到。如果不绕开这些岩石,往往团队TDD开展的热火朝天,但效果却差强人意,开发人员苦不堪言。

    本系列通过笔者在软件行业多年的沉淀,为大家梳理出一系列的TDD湖水下的岩石并一一标注,给大家提供一张航路图,供大家参考借鉴。

    其中第一块岩石就是想当然认为TDD用例是白盒。其实TDD应该是个妥妥的黑脸大汉,是黑盒测试。

TDD是XP实践环里非常重要的一个实践TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

    它位于XP实践环最内层的个人实践环中,每个环都分为拉动实践(手段)和核心实践(目标),TDD是其中最重要的拉动实践之一。
   

    TDD首先应该追求性价比,一般情况下,在分支覆盖率60%的情况下,生产代码和测试代码的比例一般会在2:1-1:5之间,在代码量巨大(100w以上的代码)的大型团队中,如果控制不好生产代码和测试代码比例,那额外增加的工作量可能是一场噩梦。
    只有高性价比才能到有效降低用例开发、调试、维护的工作量,才有可能把TDD坚持下去。
     其次,必须追求用例相对高稳定性,只有把用例写到功能单元(功能单元粒度有大有小)边界上才能保持用例尽可能不受内部实现频繁变更的影响,保持相对稳定,因为功能边界上的接口代表了外部需求,相对内部实现来说,需求一般相对都比较稳定。用例写在功能单元接口上,也即意味它就是一个毋庸置疑的黑盒用例。
    再次有的同学TDD的时候,习惯于进行流程拆解,一直拆到最小流程的才开始实施TDD,我们举个例子,类似下图TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

为了测试流程1的功能,我们有两种思路:

思路一(容易想到的方案)

TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

思路二(更简单的方案)

TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

       思路一是一种非常朴素的思路,可以做到很高的覆盖率,但是它有个问题,但思路二的一条全流程用例有基本可以覆盖思路一1-4的4条用例,可见思路二性价比更高。

    思路一造成生产代码和测试代码比例增高,增加了用例维护的工作量。

其实,思路二相当于下图TDD湖水下的岩石(一):TDD其实是个长着白盒脸的黑盒测试

    存在就是被感知:只关注外部行为,即任何一行内部实现的代码,都对系统的外部行为有所表现。

    综上,TDD本身主要不是追求用例的覆盖和测试完备,而是追求功能变更或bug fix时对已有功能的尽可能完备的回归守护。在大型项目中,生产代码和用例代码比例(TDD性价比)才是重中之重,因此,尽量端到端的黑盒UT才是平衡性价比和回归守护完备性的关键。

    那黑盒的TDD具体怎么开展呢?

TDD一般要包含两部分,需求(也可以对应故事)分析和需求实现,其中,
一、需求分析:

1、需求拆分
一般按场景拆分、按工作量拆分、按simple/complex拆分等成需求纵切的切片

2、需求实例化
拆分后的需求进行实例化,抽样数据形成例子从而阐明这个需求。

3、需求代码化
通过代码固化需求行成用例,在编写用例时根据对接口的使用定义/扩充/修订功能单元对外接口,从而满足依赖倒置原则中接口在上层定义的要求。


二、需求实现:

1、红
根据实例化的需求纵切片开发用例

2、绿
快速开发生产代码实现用例,验证用例正确性

3、蓝
重构代码去除坏味道

       从上述步奏可见,需求分析阶段是前置条件,只有做好需求分析,并将实例化的需求切片固化成用例,并在用例中定义/扩充/修订出其中使用到的功能单元的接口,这时用例自然而然就变成黑盒的。即必须做到用例先行。

    例如,有个字符串搜索需求,即在文件中搜索字符串,从命令行读入文件名和被搜索的字符串,返回结果的行。

    这时应该对功能单元外接口(比如String[] minGrep(String file,String exp))开展TDD,而不是内部各个实现函数开展TDD。并对返回结果总命中的行进行assert,这会包含其中打开文件openFile,读取内容readFile,字符串搜索search,拼装结果assemble这些内部函数进行覆盖。即这些内部实现都会对功能单元接口的在外部接口有所展现和影响。

    TDD的本质不是测试故障,它的主要目的是波及影响防护,如果后续对openFile、readFile、search、assemble任何重构,minGrep上的端到端黑盒用例都可以识别这些内部实现的变更和波及影响。

    小结,要想在大规模系统中做好TDD,必须坚持黑盒TDD,也就必须做好之前的需求纵切片的代码固化和固化过程中定义/扩充/修订的功能单元的外部接口。

发表评论

登录后才能评论
联系客服
联系客服
分享本页
返回顶部