在上一篇中,我们介绍了什么是Spock以及它和JUnit,JMock等其它测试框架的对比,从这一篇开始,我们将开始详细介绍Spock的使用。

在这一章,我们首先讲解如何在项目中引入Spock,以及Spock的一些核心概念介绍,并且抛砖引玉给出一个简单的单测示例。

引入Spock

针对Java单元测试,开发工具 IntelliJ IDEA。

<!-- 可以只引入这一个包,它已经引入了 spock-core、groovy-all; 如果有冲突可以手动指定版本引入 此包是整合springboot使用,用于在spock中启动springboot容器, 如果仅想使用spock作为单测使用,可以不引入当前包,只引入 spock-core、groovy-all 即可 --> <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-spring</artifactId> <version>1.3-groovy-2.4</version> </dependency> <!-- 可以不引入 --> <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-core</artifactId> <version>1.3-groovy-2.4</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> <version>2.4.17</version> </dependency>

在单测用例所在module的 pom文件添加plugin的配置,多module的情况下放主pom不生效

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <skipTests>false</skipTests> <parallel>classes</parallel> <threadCount>4</threadCount> <forkCount>1C</forkCount> <includes> <include>**/*Test.java</include> <include>**/*Spec.java</include> </includes> </configuration> </plugin> <plugin> <!--groovy plugin--> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.4</version> <extensions>true</extensions> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <!-- spock单测文件路径 --> <testSources> <testSource> <directory>${project.basedir}/src/test/java</directory> <includes> <include>**/*.groovy</include> </includes> </testSource> <testSource> <directory>${project.basedir}/src/test/groovy</directory> <includes> <include>**/*.groovy</include> </includes> </testSource> </testSources> </configuration> </plugin>

  • 引入过程中的注意点

问题1:java.lang.NoClassDefFoundError: org/codehaus/groovy/transform/stc/AbstractExtensionMethodCache

解决:将groovy 和 groovy-all 都保持2.4.17版本

问题2:配置了gmavenplus-plugin并指定了路径但不生效

配置在主pom中,但单测在B模块中导致,将插件配置到A或B模块中

主pom

|----A

|----B

问题3:引入groovy依赖后可能会出现版本冲突的问题

因为如果你的项目引用了springboot-start-base这样的集合式jar包,它里面也会引用groovy,有可能跟我们引入的groovy包版本出现冲突,或者公司的一些框架也会引用groovy的包,如果版本不一致也有可能冲突,需要排下包。

这里推荐idea的maven插件 MavenHelper 能可视化快速排查依赖问题。

spock常见问题 Spock单元测试框架系列(1)

MavenHelper依赖分析

Spock核心概念
  • Spock标签

标签

作用

备注

given

为所测试方法做一些前置准备工作的地方

一般将方法内部依赖的参数在这个标签内完成

when

执行测试

when 和 then 必须成对出现

then

验证测试结果是否符合预期

when 和 then 必须成对出现

expect

精简版when then

expect里代码只可以包含布尔表达式

and

承接上一个标签

where

用于编写数据驱动的用例方法

总是在方法的最后,且不能重复

cleanup

测试方法退出前执行一些清理工作

一个 cleanup 后仅仅只能跟一个where ,即使前面发生了异常cleanup也会运行(类似于Java的finally)

  • Mock、Stub和Spy区别

方法

描述

Mock()

Mock的对象是一个虚拟类,为每个方法调用返回了一个默认值,用于替换真实的类。Mock()不仅可以模拟方法返回结果,还可以模拟方法行为,比如验证某个方法是否被调用以及调用次数

Stub()

Stub的对象也是一个虚拟类,比Mock()更简单些。只返回事先准备好的测试数据,而不提供交互验证

Spy()

又叫刺探方法,它会包装一个真实的对象,默认情况下将调用真实的方法。Spy()功能最强大,但这样做意味着代码可能有坏味道,设计上可能有问题。

Spock单测示例

待测试类:

public class square { private final int length; public Square(int length) { this.length = length; } public int area() { return length * length; } public int perimeter() { return length * 4; } }

测试类:

class SquareTest extends Specification { @Unroll def "given square length: #length, square area: #area, square perimeter: #perimeter"() { given: def square = new Square( length: length ) expect: square.area() == area square.perimeter() == perimeter where: length || area | perimeter 1 || 1 | 4 2 || 4 | 8 } }

def 是groovy的关键字,可以用来定义变量和方法名,这里可以用英文也可以用中文。

expect...where... expect为核心的测试校验语句块,where则是多个测试用例的举例,多个列使用"|"单竖线隔开,"||"双竖线区分输入和输出变量,即左边是输入值,右边是输出值。表格列的长度不一样,手动对齐比较麻烦,intellij idea支持format格式化快捷键。

spock常见问题 Spock单元测试框架系列(2)

上面图中定义的测试方法名使用了groovy的字面值特性,即把请求参数值和返回结果值的字符串动态替换掉,"#length、#area、#perimeter" #号后面的变量是在方法内部定义的,前面加上#号,实现占位符的功能。

@Unroll 注解,可以把每一次调用作为一个单独的测试用例运行,这样运行后的单测结果更直观:

spock常见问题 Spock单元测试框架系列(3)

如果其中一行测试结果错误,spock的错误提示信息也很详细,方便我们排查(比如我们把第2行测试用例返回的perimeter改成4)

spock常见问题 Spock单元测试框架系列(4)

总结

在这篇文章中,我们引入了Spock,并对Spock的一些概念做了简单介绍,最后给出一个简单的测试示例,进一步加深对Spock的理解。下一篇章我们将会详细的介绍如何通过Spock测试复杂的if-else场景。

,