如何写一个好的测试

博客原文地址java

背景

在上一个项目上,因为项目成员大部分是新入职的同事,因此对于测试不是很熟悉,
这就致使了在项目前期,项目上的不少测试都不太make sense,虽然没有什么定量的东西来描述,
可是总结起来就2个点:git

  1. 测试的名字比较模糊。
  2. 测试代码不易读。

深刻剖析

测试名字比较模糊

对于这一个问题,是由于不少刚开始写测试的开发脑子里不会快就想到given、when、then这三个词,
通常咱们写测试写得比较多的同事,都会使用should_return_xxxx_when(if)_xxxx。其实就是在脑子中
想到了given、when、then。而新同事照着模仿的时候,极可能就单纯地写了should_xxx。他们只考虑告终果,
可是没有考虑条件,这就致使了读名字仍是get不到测试想要干什么。github

对于这个问题,我想起了多年前在stackoverflow上面看到的一种写测试名字的模板,他是这样推荐的:
GivenA_WhenB_C,我以为这中写法挺好的,因此在项目中用了起来,结合实际使用,我又对此提出了两个改进:测试

  1. given when这两个词能够省略,固然前提是咱们约定了最前面就是given,中间就是when,最后就是then。
    经过施加规则限制来缩短测试名。
  2. 测试名字经常很长,一大堆驼峰其实比较不容易阅读。因此可使用蛇形命名法,可是这样就须要想一个符号来
    分隔given、when、then了,我选择使用___(也就是3个_),由于通过试验,发现2个_过短了不容易一眼看出分隔
    4个则又没有必要。

最终的效果是这样的(这是一个例子,来自最近我参加的DDD进阶培训中的训练题):ui

parking_order_is_natural_order___park_cars_by_parking_assistant___car_parked_to_correct_parking_lot_in_turn
car_is_already_took___take_back_car_with_used_receipt___exception_is_thrown
parking_lot_is_available___park_a_car_by_parking_assistant___receipt_returned
car_is_parked___take_back_car_with_invalid_receipt___exception_is_thrown

这里还有一个小技巧,若是一个测试真的没有given的时候,或given不重要的时候,能够省略,可是___不能省略:code

___xxx_xxx___xxx_xxxip

能够总结一下这种命名方式的优势:开发

  1. 能制定一个规则的话,项目上的测试标题能更容易统一(能够说是统一语言了)。还能够加上静态检查,使得一些名字不规范的测试提早被发现
    名字不规范,说明是新进项目的同事写的,确实要重点检查一下。
  2. 强制规定出given、when、then,那么,咱们在写测试的时候,就会被强迫想清楚咱们的测试要作什么。
  3. 结构化的东西更适合大脑阅读,读测试的时候更容易,咱们不须要先读一遍测试名才能提取到given、when、then,能够一眼就定位出三个部分。

固然,也是有缺点的:
我用这种方式写出来的测试名字通常都比较长,这个有多是我用词还不够精炼,在given、when的部分可能有时候是有一部分重复的
因此也须要刻意练习,学会精简用词。get

最后,给这种命名方式命个名吧,就叫GWT测试命名法好了。博客

测试代码不易读

我在项目上发现,不少人习惯在构建fake数据的时候直接将全部信息屏蔽,就提供一个createX()的方法,
在createX的方法里面可能还要构建X的构成部分:

X createX() {
Y yyy = new Y;
X xxx = new X(YYY, field1, field12);
return xxx;
}

除了createX外还有createAX、createBX,而后在不一样的测试里面混合调用。
做为看测试的我,我就很难看到到底须要一个怎样的场景,field1究竟是怎么设置的,field2究竟是怎么设置
的,并且在想改一下createX的时候,还会牵扯到其余的测试莫名其妙就挂了。

我目前使用的解决方式是这样的:

  1. 先要肯定好测试要涉及到的重要信息,好比上面若是field1,field2都会对测试的逻辑起到做用,
    那么,即便冗余,也必定要写在测试方法内。好比:

    @Test
    void ___xxx___xxx() {
    Y y = createY();
    X x = createX(field1, field12, y)
    // do some thing
    // assert some thing
    }

    这么作的好处是,当我要看某一个测试的时候,它的前置数据我一眼就能看出来,不须要不停地command+b跟进去才知道须要什么。

  2. 在应用第一条原则的时候,必定要记得,只罗列出这个测试所关心的数据。假如field3彻底不参与此次
    逻辑的处理,又必需要有值,那么,在createX内部给个默认值便可,不须要放在createX参数列表中。

为何不适用builder?

其实以前也试过用builder,可是看起来太多了,适用create的方式能缩短要写得代码行数,更容易一眼看完测试。

相关文章
相关标签/搜索