Spock写单测

官网:http://spockframework.org/
文档:http://spockframework.org/spock/docs/
github:https://github.com/spockframework

官方介绍:

Spock is a testing and specification framework for Java and Groovy applications.
What makes it stand out from the crowd is its beautiful and highly expressive specification language.
Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers.
Spock is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.

spock是一个测试框架,它的核心特性有以下几个:

  1. 可以应用于java或groovy应用的单元测试框架。
  2. 测试代码使用基于groovy语言扩展而成的规范说明语言(specification language)。
  3. 通过junit runner调用测试,兼容绝大部分junit的运行场景(ide,构建工具,持续集成等)。
  4. 框架的设计思路参考了JUnit,jMock,RSpec,Groovy,Scala,Vulcans……

依赖


    org.spockframework
    spock-spring
    1.3-groovy-2.4
    test

groovy编译插件


        
            
                org.codehaus.gmavenplus
                gmavenplus-plugin
                1.6
                
                    
                        
                            addTestSources
                            compileTests
                        
                    
                
            
        

重要概念:

1. Specification

在Spock中,待测系统(system under test:SUT) 的行为是由规范(specification) 所定义的。在使用Spock框架编写测试时,测试类需要继承自Specification类

2. Fixture Methods

预先定义的几个固定的函数,与junit或testng中类似
def setup() {} // run before every feature method
def cleanup() {} // run after every feature method
def setupSpec() {} // run before the first feature method
def cleanupSpec() {} // run after the last feature method

3. Feature methods

这是Spock规范(Specification)的核心,其描述了SUT(system under test)应具备的各项行为。每个Specification都会包含一组相关的Feature methods,如要测试1+1是否等于2,可以编写一个函数:

def "sum should return param1+param2"() {
    expect:
    1+1 == 2
}

4. blocks

每个feature method又被划分为不同的block,不同的block处于测试执行的不同阶段,在测试运行时,各个block按照不同的顺序和规则被执行

  • Setup Blocks:
    setup也可以写成given,在这个block中会放置与这个测试函数相关的初始化程序,一般会在这个block中定义局部变量,定义mock函数等
  • Cleanup Blocks:
    函数退出前做一些清理工作,如关闭资源等。
  • When and Then Blocks:
    when与then需要搭配使用,在when中执行待测试的函数,在then中判断是否符合预期
  • Expect Blocks:
    是When and Then的简版
  • 断言:
    条件类似junit中的assert,就像上面的例子,在then或expect中会默认assert所有返回值是boolean型的顶级语句。如果要在其它地方增加断言,需要显式增加assert关键字
  • 异常断言:
    见例1
  • Where Blocks:
    做测试时最复杂的事情之一就是准备测试数据,尤其是要测试边界条件、测试异常分支等,这些都需要在测试之前规划好数据。但是传统的测试框架很难轻松的制造数据,要么依赖反复调用,要么用xml或者data provider函数之类难以理解和阅读的方式。
    例子见例2

5. Interaction Based Testing

mock and Stubbing:
见例3

现实中的场景往往会比例子复杂(比如要mock一个private函数,或者全局变量,或者静态函数,等等),但是此时更好的思路并不是压榨框架的功能,而应该是去思考代码的设计是否出了问题。

被测试的Java Class

import com.xxx.exception.CustomException;
public class TestImpl {
    public int publicVariable;
 
    public int testInt(Integer i) throws CustomException {
        System.out.println("publicVariable:" + this.publicVariable);
        if (i == null) {
            throw new CustomException("msg", 123123123);
        }
        System.out.println(this.calculate(i));
        System.out.println(this.calculate(i));
        return this.calculate(i);
    }
 
    public int calculate(Integer i) {
        return i+1;
    }
}

Spock单测代码:

import com.xxx.exception.CustomException
import spock.lang.Specification
import spock.lang.Unroll
 
class SpockTest extends Specification {
    private testClass
 
    void setup() {
        this.testClass = Mock(TestImpl)
        this.testClass.publicVariable = 2
//        def tmpImpl = new TestImpl(publicVariable: 2)
    }
 
    void cleanup() {
    }
 
    def "test int -(7&*."() {
        when:
        def result = this.testClass.testInt(param)
 
        then:
        result == expectResult
 
        where:
        param | expectResult
        1     | 2
        2     | 3
    }
 
    def "test when then"() {
        when:
        def result = this.testClass.testInt(1)
 
        then:
        result == 2
        1 * this.testClass.calculate(_)
    }
 
    def "test expect"() {
        expect:
        this.testClass.testInt(1) == 2
    }
 
    //例1
    def "exception"() {
        setup:
        def listNull = null
        def list = new ArrayList()
 
        when:
        list.pop()
        then:
        thrown(NoSuchElementException)
        list.isEmpty()
 
        when:
        this.testClass.testInt(null)
        then:
        def e = thrown(CustomException)
        e.getCode() == 123123123
        //另一种写法:
//        CustomException e1 = thrown()
//        e1.getCode() == 123123123
 
        when:
        listNull.pop()
        then:
        notThrown(NullPointerException)
    }
 
    //例2-1
    def "test no data tables"() {
        expect:
        Math.max(1, 3) == 3
        Math.max(7, 4) == 7
        Math.max(0, 0) == 0
    }
    //例2-2
    //实际会跑三次测试,相当于在for循环中执行三次测试,a/b/c的值分别为3/5/5,7/0/7和0/0/0。如果在方法前声明@Unroll,则会当成三个方法运行
    @Unroll
    def "test data tables"() {
        expect:
        Math.max(a, b) == c
        where:
        a | b | c
        3 | 5 | 5
        7 | 0 | 7
        0 | 0 | 0
    }
    //例2-3 批量依次赋值
    @Unroll
    def "test data tables batch"() {
        expect:
        Math.max(a, b) == c
 
        where:
        a || _
        3 || _
        7 || _
        0 || _
        1 || _
        b << [5, 0, 0]
        c = a > b ? a : b
    }
 
    //例3
    def "test mock and stubbing"() {
        setup:
        this.testClass.calculate(_) >> 3
 
        when:
        def result1 = this.testClass.testInt(1)
 
        then:
        result1 == 3
        1 * this.testClass.calculate(_) >> 3 //any single argument (including null)
//        1 * this.testClass.calculate(_) >> {throw new Exception()} //Mock对应函数抛异常
//        3 * this.testClass.calculate(_) >>> [1,2,3] //每次调用返回不同结果
//        1 * this.testClass.calculate(*_) >> 1 //any argument list (including the empty argument list)
//        1 * this.testClass.calculate(_ as Integer) >> 1 // any non-null argument that is-a Integer
//        1 * this.testClass.calculate(!null) >> 1 // any non-null argument
//        (1..3) * this.testClass.calculate(_) // between one and three calls (inclusive)
//        (1.._) * this.testClass.calculate(_) // at least one call
//        (_..3) * this.testClass.calculate(_) // at most three calls
//        1 * this.testClass._(*_) // any method on subscriber, with any argument list
    }
}

你可能感兴趣的:(Spock写单测)