背景
项目维护的久了,业务逻辑就会因为需求导致越来越多的分支,也可能在开发过程中cv的时候疏忽,忘记修改了一个参数,直到上线的时候bug才体现出来。这就陷入了一个没有测试导致开发过程中的bug带入了线上环境的尴尬境地。
实际上一直都想找个时间对项目写一份测试,正常的测试框架实在太难用,再加上没有太多的时间,所以这个事情一拖再拖,在前阵子无意中看到了一款测试框架——Spock,稍微了解了一下后想尝试一下这个框架来编写测试。前几天终于忙完了OJ的改造,立马就来玩玩spock了。
网上关于Spock的资料比较简单,包括官网的demo也是如此,并且因为spock2增加了很多特性(目前还在不断的更新中),网上的资料存在过多的过时内容,所以在写的过程中遇见了无数的问题,不过总算是解决了,所以写篇博客来记录一下。
Junit单元测试的痛点
现在我有一个AccountInput的DTO用来接收前端请求,其父类BaseDTO提供了2个final方法:
convert
:将dto转换成对应的实体类convertAndUpdate
:将DTO中的字段对实体的相应字段进行覆盖。
但是有些字段我不需要覆盖怎么办,所以额外提供了一个@CopyIgnore
注解用于忽略 convert/update 过程中某些不需要覆盖的字段
1 |
|
1 | /** |
然后我们就针对这部分逻辑取编写一份正常的junit测试来康康,为了确保测试的完善,我们的测试肯定得有多组输入,多组输入的方式有很多种,这里我们先采用一个相对简单方便的@CsvSource
来进行多组测试用例的输入。
1 | /** |
通过上面的代码我们可以发现,所有参数都是通过字符串方式进行传递然后通过切面获取测试的参数类型然后再转换成对应的类型进行注入的。在默认情况下空字符串会视为null
进行参数注入,那么如果我们需要注入空字符串给String对象怎么办?手动将CsvSource#nullValues()
设置为一个代表null
的字符串即可(例如"None"
),但是这又引入了一个新的问题,我有些时候需要None这个字符串怎么办,所以这时候就陷入了对象通过字符串表达造成矛盾之中。
通过@Parameters
与DataProvider
的方式虽然可以解决,但是无论哪个方法都比较复杂,往往一个十几行的代码,需要几十行甚至上百行的代码进行测试,而且采用@Parameters
进行参数注入,需要构造函数进行配合,那么就变成了需要一整个测试类来测试一个方法。
1 | /** |
不过最让我动心的还是spock对于条件分支测试的直观程度,这里上个图举个简单的例子(当然这个例子举的可能不够好)
如果在if/else
很多的复杂场景下,编写测试代码的成本就很高,为了分支的覆盖率,编写的测试代码长度可能远远超过了被测试的代码。当然JUnit的@Parametered参数化注解或者DataProvider可以解决多数据分支问题,但是编写起来非常麻烦,而且不够直观,如果某个用例出错报错也不够详细。
但是spock的where标签,基于spock得天独厚的数据表语法糖,测试代码覆盖所有的if分支逻辑,我们要做的仅仅是编写一份表格,就能完成一份复杂的分支测试,开发效率更高,更适合敏捷开发。