SPOCK 一个基于Groovy 的单元测试框架
一、Spock 是什么
简单地说,spock是一个测试框架,他的核心特性有以下几个:

【1】可以应用于java或groovy应用的单元测试框架。
【2】测试代码使用基于groovy语言扩展而成的规范说明语言specification language。
【3】通过junit runner调用测试,兼容绝大部分junit的运行场景(ide,构建工具,持续集成等)。
【4】框架的设计思路参考了JUnit,jMock,RSpec,Groovy,Scala,Vulcans……
Groovy是Java平台上设计的面向对象编程语言。这门动态语言拥有类似Python、Ruby和Smalltalk中的一些特性,可以作为Java平台的脚本语言使用。
Groovy的语法与Java非常相似,以至于多数的Java代码也是正确的Groovy代码。Groovy代码动态的被编译器转换成Java字节码。由于其运行在JVM上的特性,Groovy可以使用其他Java语言编写的库。
【1】轻量级、对于java程序员来说门槛低
【2】完全兼容java语法
【3】提供脚本语言灵活性、元编程、specification language
二、为什么使用 Spock
【1】spock框架使用标签分隔单元测试中不同的代码,更加规范,也符合实际写单元测试的思路
【2】代码写起来更简洁、优雅、易于理解
【3】由于使用groovy语言,所以也可以享受到脚本语言带来的便利
【4】底层基于jUnit,不需要额外的运行框架
【5】已经发布了2.0-M版本,基本没有比较严重的bug
def "test getTransportTypeNameConfig"(){
given:
enumMapConfig.put("name.type.transport:"+transportType,expecyName);
when:
String actualTypeName = settingContext.getTransportTypeNameConfig(transportType);
then:
expecyName == actualTypeName;
where:
transportType | expecyName
"A" | "AType"
"B" | "BType"
null | ""
}
三、使用 Spock
【1】maven引入spock框架
<!-- Mandatory dependencies for using Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
</dependency>
【2】 Groovy语言支持
<dependency> <!-- use a specific Groovy version rather than the one specified by spock-core -->
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.5.9</version>
</dependency>
由于spock是基于groovy语言的,所以需要创建groovy的测试源码目录:首先在test目录下创建名为groovy的目录,之后将它设为测试源码目录

def "test getTransportTypeNameConfig"(){
given:
enumMapConfig.put("name.type.transport:"+transportType,expecyName);
when:
String actualTypeName = settingContext.getTransportTypeNameConfig(transportType);
then:
expecyName == actualTypeName;
where:
transportType | expecyName
"A" | "AType"
"B" | "BType"
null | ""
}
四、Spock 基本概念
Specification等同于 测试类,继承自spock.lang Specification基类, Specification是一个Groovy类,提供一些有用的方法,verifyAll(Coloure)、With、Throw还有Sputnik, Spock’s JUnit runner.、Mock之类的
class MyFirstSpecification extends Specification {
// fields
// fixture methods
// feature methods
// helper methods
}
Methods 夹具
def setupSpec() {} // runs once -before the first feature method
def setup() {} // runs beforeevery feature method
def cleanup() {} // runs afterevery feature method
def cleanupSpec() {} // runs once -after the last feature method
feature method这个是spock测试代码的核心,类似于Junit中的TestMethod,支持使用 字符串字面量命名,这样可以得到更高的可读性。
Conceptually, a feature method consists of four phases:
1. Set up the feature's fixture
2. Provide a stimulus to the system under specification
3. Describe the response expected from the system
4. Clean up the feature's fixture
Blocks每个feature method又被划分为不同的block,不同的block处于测试执行的不同阶段,在测试运行时,各个block按照不同的顺序和规则被执行
Given Blocks 主要做初始化
given: def stack = new Stack()
def elem = "push me”
When and Then Blocks ,调用被测试逻辑,执行测试条件

Conditions
测试目标类
def "HashMap accepts null key"(){
given:
def map = new HashMap()
when:
map.put(null, "elem")
then:
notThrown(NullPointerException)
}
when:
stack.push(elem)
then:
!stack.empty
stack.size()==1
stack.peek()== elem
异常测试
GROOVY
when :
stack.pop()
then :
thrown(EmptyStackException)
stack.empty
Conditions 方法调用
有时候可能需要检查 依赖的方法背调用,调用、几次、参数是啥
def "events are published to all subscribers"() {
given:
def subscriber1 = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
def publisher = new Publisher()
publisher.add(subscriber1)
publisher.add(subscriber2)
when:
publisher.fire("event")
then:
1 * subscriber1.receive("event")
1 * subscriber2.receive("event")
1 * subscriber.receive("hello")
// an argument that is equal to the String "hello"
1 * subscriber.receive(!"hello")
// an argument that is unequal to the String "hello"
1 * subscriber.receive()
// the empty argument list (would never match in our
example)
1 * subscriber.receive(_)
// any single argument (including null)
1 * subscriber.receive(*_)
// any argument list (including the empty argument list)
1 * subscriber. receive(!null)
// any non-null argument
1 * subscriber.receive(_ as String)
// any non-null argument that is-a String
1 * subscriber. receive(endsWith("lo")) // any non-null argument that is-a String
1 * subscriber.receive({ it.size()>3 &&it.contains('a')})
// an argument that satisfies the given predicate, meaning that
// code argument constraints need to return true of false
// depending on whether they match or not
// (here: message length is greater than 3 and contains the character a)
}
Conditions —— Expect BlockWhen和Then 写到一起就是Expect
when:
def x= Math.max(1, 2)
then :
×==2
expect:
Math.max(1,2) == 2
given:
def file = new File("/some/path")
file.createNewFile()
//...
cleanup:
file.delete()
Where Blocks 与Data Driven Testing
写好测试逻辑,使用不同的数据测试,方便对边界检查等,还有一些技巧,可以发挥想象力
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b)== c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
五、Spock Mocking
def subscriber = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
Subscriber subscriber = Mock()
Subscriber subscriber2 = Mock()
subscriber.receive(_) >> “ok”每次都返回“ok”
subscriber.receive(“message1”) >> “ok”参数是message1时返回OK
subscriber.receive(_) >>> [“ok”, “error”, “error”, “ok”]返回序列值
subscriber.receive(_) >> { args -> args[0].size() > 3 ? “ok” : “fail” }你想返回什么值
subscriber.receive(_) >> { throw new InternalError(“ouch”) } 异常
ubscriber.receive(_) >>> [“ok”, “fail”, “ok”] >> { throw new InternalError() } >> “ok”组合起来
given:
subscriber.receive("message1")>"ok"
when :
publisher.send("message1")
then :
1 * subscriber.receive("message1")
Where Blocks 与Data Driven Testing
写好测试逻辑,使用不同的数据测试,方便对边界检查等,还有一些技巧,可以发挥想象力
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b)== c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
更多推荐


所有评论(0)