Moje zdjęcie
Software Craftsman's Blog by Marcin Pieciukiewicz
Java and Scala development

Monday, March 18, 2013

Spock testing framework introduction

During 33rd Degree conference that took place in Warsaw in the middle on March 2013 I was listening to Ken Sipe’s speech entitled Mocking, Stubbing and Spying with Spock. This has been very interesting and enjoyable introduction (at least for me) to Spock testing and specification framework.

What is Spock?

Spock is a testing framework for Java Virtual Machine. It is developed in Groovy but is perfecly usable to test Java programs. Spock focuses on developing an easy to read and write specifications that extensively uses Groovy language features.

So take a look how it might be used to test application, i.e. a simple calculator that is implemented with this Java code:

package pl.marpiec.calculator;

public class Calculator {
    private int a;
    private int b;

    public Calculator(int a, int b) {
       this.a = a;
       this.b = b;
    }

    public int add() {
       return a + b;
    }

    public int subtract() {
       return a - b;
    }

    public int multiply() {
       return a * b;
    }
}
We want to test it’s ability to add, subtract and multiply features. To do it with spock we have to create groovy class that extends spock.lang.Specification class. And inside we can define a test method, i.e.:

package pl.marpiec.calculator

import spock.lang.Specification
import spock.lang.Unroll

class CalculatorSpec extends Specification {

    def "Addition of two integers"() {
       given:
       def calculator = new Calculator(a, b)

       expect:
       calculator.add() == sum

       where:
        a    |  b    ||  sum
       -5    |  3    || -2
        2    |  4    ||  6
        8    | -4    ||  4
        5    |  0    ||  5
    }
}
Let’s have a closer look to this method, because it looks quite strange for java developers.
First. Groovy allows you to name methods using quotes to define names containing spaces. That allows you to use more natural language, and easier to represent business requirements.
Second. Our test method is divided into three blocks named “given”, “expect”, “where”. First is used to initialize our code, second one to define assertions (you might thing of it as it was assertTrue(calculator.add() == sum)), and the last one defines our test data in easy to read tabular form.
Third. Specification defined in this way is in reality 4 tests with input data given in the where: block.
What is going on?To perform a test Spock takes data defined in where: block and puts it into test code specified in given and expect code and then performs the tests.

As you can see Spock’s Specification is very easy to read and define test data. What is also great in Spock is how it outputs the information about failed tests. In example let’s write another test method to test subtract feature of Calculator class, it is in purpose defined a little differently than the first one to enhance Spock abilities:
    def "Subtraction of two integers"() {
       expect:
       new Calculator(a, b).subtract() == difference

       where:
        a   |  b   ||  difference
       -5   |  3   || -8
        2   |  4   || -2
        8   | -4   ||  12
        5   |  0   ||  5
    }
If we than broke the subtract method code to and fire the test:
    public int subtract() {
       return b - a;
    }
The output of the test is this:
Subtracting of two integers(pl.marpiec.calculator.CalculatorSpec)  Time elapsed: 0.625 sec  <<< FAILURE!
org.spockframework.runtime.SpockComparisonFailure: Condition not satisfied:

new Calculator(a, b).subtract() == difference
|              |  |  |          |  |
|              -5 3  8          |  -8
|                               false
pl.marpiec.calculator.Calculator@1c54796

       at pl.marpiec.calculator.CalculatorSpec.Subtracting of two integers(CalculatorSpec.groovy:25)

This is realy beutiful test output, isn’t it? Spock writes out every importany value used in assertion, so no debugging is required to check what went wrong.

And the last feature example will help us to determine what input data was used to performe a test. That is especially helpful if we would’nt use the input data directly in the expect: block, as it was done in method that tested addition. In spock we could use parameters in method name(!) using #variableName convention. Lets create test method for multiply feature of Calculator:
    @Unroll
    def "Multiplication of two integers (#a * #b = #product)"() {
       given:
       def calculator = new Calculator(a, b)

       expect:
       calculator.multiply() == product

       where:
        a   |  b   ||  product
       -5   |  3   || -15
        2   |  4   ||  8
        8   | -4   || -32
        5   |  0   ||  0
    }

The important parts are @Unroll adnotation that allows us (among the others) to use parameters in method name, and expression “#a * #b = #product” inside the method name. Now when error fails we’ll see the following output:
Multiplying of two integers (-5 * 3 = -15)(pl.marpiec.calculator.CalculatorSpec)  Time elapsed: 0.11 sec  <<< FAILURE!
org.spockframework.runtime.SpockComparisonFailure: Condition not satisfied:

calculator.multiply() == product
|          |          |  |
|          -2         |  -15
|                     false
pl.marpiec.calculator.Calculator@147358f

       at pl.marpiec.calculator.CalculatorSpec.Multiplying of two integers (#a * #b = #product)(CalculatorSpec.groovy:41)


Those of course aren’t all the Spock’s features, but I hope this is a good example to start with Spock. I’m for sure will digg into other Spock's features.

The example code with maven build definition can be found on github here HelloSpock.

No comments:

Post a Comment