spock使用文档

本文详细介绍了Spock框架的使用,包括Maven配置、测试类声明、固定方法、测试方法和块的使用,如setup、when/then块、expect块和cleanup块。讲解了数据驱动测试、交互驱动测试以及Mock对象的创建、默认行为和验证。还讨论了Spock与其他单元测试框架的比较,以及Spock的扩展注解,如@Timeout、@Ignore和@Retry。
摘要由CSDN通过智能技术生成

spock 使用

maven

spock 1.x 版本(推荐)

        <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-all</artifactId>
            <version>2.4.6</version>
        </dependency>

Spock自带Mock功能,所以我们可以来Mock非静态方法。但是遇到静态方法时,我们需要导入powermock

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>2.0.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>2.0.0</version>
            <scope>test</scope>
        </dependency>

但是当我们需要测试dao层的sql语句时,我们可以结合H2内存数据库使用,此时需要引入:

        <dependency>
            <groupId>com.github.janbols</groupId>
            <artifactId>spock-dbunit</artifactId>
            <version>0.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.5.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
            <scope>test</scope>
        </dependency>

导入

导入 Spock 框架基础包 import spock.lang.*。

声明测试类

测试类是一个 Groovy 类,必须继承 spock.lang.Specification

class MyFirstSpec extends Specification {
  // 字段
  // 固定方法
  // 测试方法
  // 辅助方法
}

字段

非静态字段可以在固定方法中使用,推荐在声明时同时初始化(在 setup() 方法中初始化也是一样的)。但是不能在测试方法之间共享,每个测试方法都有一个隔离的副本,每个测试方法开始前都会初始化一遍。

def obj = new ClassUnderSpecification()
def coll = new Collaborator()

但是有时候又需要在测试方法之间共享字段,比如这个字段的初始化的开销很大,或者需要在测试方法间通信。通过将字段标记为 @Shared,可以使字段成为共享字段。还是推荐在声明时初始化(在 setupSpec() 方法中初始化也是一样的)。

@Shared res = new VeryExpensiveResource()

固定方法

def setup() {}          // 每个测试方法开始前都会执行一遍
def cleanup() {}        // 每个测试方法后都会执行一遍
def setupSpec() {}     // 在第一个测试方法开始前执行一遍
def cleanupSpec() {}   // 最后一个测试方法后执行

固定方法可以有也可以没有,但是建议要有 setup() 和 cleanup()。注意 setupSpec() 和 cleanupSpec() 中不可以引用非共享字段。

这四个固定方法其实是重写自 spock.lang.Specification,setup() 和 setupSpec() 的执行顺序是先父类后子类,cleanup() 和 cleanupSpec() 的执行顺序是先子类后父类。不需要显式调用父类的对应方法。

测试方法

def "pushing an element on the stack"() {
  // 测试方法
}

测试方法是测试类的核心。测试方法名是一个字符串常量,也是对测试方法的描述。一个测试方法应该由4部分组成:

  1. Setup:环境初始化(可选)
  2. Stimulus:调用待测试代码
  3. Response:描述预期行为
  4. Cleanup:清理资源(可选)

Spock 对这四个概念上的阶段提供了内建的支持,也就是“块”(block)。一个块从一个标签开始直至另一个标签或方法结尾。有6个块:setup,when,then,expect,cleanup,where。从方法的开头到第一个标签之间的所有语句都属于隐式的 setup 块。

一个测试方法至少要有一个显式声明的块,一个测试方法之所以是一个测试方法就是因为它有显式块。块把测试方法划分成不同的区域,且块不可以嵌套。

下图展示了具体的块和测试方法的4个概念阶段的对应关系:
image

其中,where 块比较特殊,后面会再提,先看其他5个块。

setup 块
setup:
def stack = new Stack()
def elem = "push me"

在 setup 块中应对当前测试方法的环境进行初始化。该块是一定是第一个执行的,且不会由于其他测试方法的存在而反复执行(和 setup() 的区别)。setup: 标签可以省略。given: 标签是 setup: 标签的别名。

when 和 then 块
when:   // 调用待测代码
then:   // 描述期望的行为

when 和 then 块是绑定在一块使用的,这两个块组合在一块使用可以调用待测代码并指定期望的行为。when 块可以是任意代码,但是 then 中的语句仅限于布尔表达式(省略了 assert),异常情况判断语句(thrown() 和 notThrown())和变量定义语句。一个测试方法中可以有多个 when-then 块。注意 then 块不宜过于庞大,尽量在5个语句之内。

关于 thrown() 和 notThrown()
这两个方法用于描述 when 块中是否应该抛出异常,参数传入异常类型。比如从一个空栈中弹栈应该抛出 EmptyStackException,为了描述这一行为,应该这样写:

when:
stack.pop()

then:
thrown(EmptyStackException)
stack.empty

thrown() 后可以跟其他条件和其他块。特别地,thrown() 返回抛出的异常:

when:
stack.pop()

then:
def e = thrown(EmptyStackException)
e.cause == null

或者另一种写法:

when:
stack.pop()

then:
EmptyStackException e = thrown()
e.cause == null

推荐第二种写法,因为可读性更强,且变量 e 的类型明确指定,方便 IDE 提供代码补全。

有时候我们期望的行为是没有任何异常出现,比如 HashMap 可以接受一个 null 键:

def "HashMap accepts null key"() {
  setup:
  def map = new HashMap()
  map.put(null, "elem")
}

这样写是可以的但是不好,因为没有明确地指出期望的行为,即没有异常出现。这样写更好:

def "HashMap accepts null key"() {
  setup:
  def map = new HashMap()

  when:
  map.put(null, "elem")

  then:
  notThrown(NullPointerException)
}

通过 notThrown() 可以更明确地指出 when 块不期望抛出 NullPointerException 异常。如果抛出了其他类型的异常,测试依然会不通过。

expect 块

expect 块可以看成是 then 块的一种简化版,只能包含布尔表达式和变量定义。当调用待测代码和描述期望行为都很简单,在一个表达式中就能搞定时,用 expect 块更简单。举个栗子,测试 Math.max() 方法:

when:
def x = Math.max(1, 2)

then:
x == 2
expect:
Math.max(1, 2) == 2

两种方式都是一样的,但是明显第二种更简洁。

cleanup 块
setup:
def file = new File("/some/path
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值