Spock

Groovy method names

Any string can be method name in groovy

def 'Should return true if number is even'() {
  //...
}
  

Simple test

@Test
def 'Should return true if number is even'() {
  when:
  def result = service.isEven(2)

  then:
  result == true
  result
}
  

Everything after then: should resolve to true

Test with mock

@Test
def 'Should return surname Stawicki if firstname is Paweł'() {
  setup:
  def Dao dao = Mock()
  def dbService = new DbService(dao)
  dao.findByFirstname("Paweł") >> new Person(
          firstname: "Paweł", surname: "Stawicki")

  when:
  def result = dbService.getSurname("Paweł")

  then:
  result == "Stawicki"
}
  

Mock method return specified in then

@Test
def 'Should return surname Stawicki if firstname is Paweł. Simplier method'() {
  setup:
  def Dao dao = Mock()
  def dbService = new DbService(dao)

  when:
  def result = dbService.getSurname("Paweł")

  then:
  result == "Stawicki"
  1 * dao.findByFirstname("Paweł") >> new Person(
          firstname: "Paweł", surname: "Stawicki")
}
  

Common initialization in setup() method

def setup() {
  dao.findByFirstname("Paweł") >> new Person(firstname: "Paweł", surname: "Stawicki")
  dao.findByFirstname("Leszek") >> new Person(firstname: "Leszek", surname: "Gruchała")
}
  

Common variables are new for each test

class TestMocksRenewal extends Specification {

  //Different for every test.
  //Every test starts with counter 0.
  def counter = 0
  def Counter objectCounter = Mock()
  

unless marked with @Shared

  @Shared
  def sharedCounter = 0
  

Mock call matchers

dao.findByFirstname("Paweł")      //Specific argument
dao.findByFirstname(_)            //Any argument
dao.findByFirstname(_ as String)  //Any String
dao.findByFirstname(!null)        //Non null
dao.findByFirstname(!"Paweł")     //Everything that's
                                  //not equal to "Paweł"
dao.findByFirstname({it.length() > 3})  //Any argument which
                                        //method length()
                                        //result is greater than 3
  

Define mock return with closures

setup:
def Dao dao = Mock()
dao.persist(_) >> { it[0] }
  

Verify parameter passed to mock with closures

then:
1 * dao.persist({ it.length() > 3 })
  

Parametrized methods

@Test
@Unroll
def 'isEven should return #isEven for #number'() {
  when:
  def result = service.isEven(number)

  then:
  result == isEven

  where:
  number  | isEven
  2       | true
  3       | false
  65      | false
}
  

Parametrization from external sources

@Test
@Unroll
def 'isEven should return #isEven for #number'() {
  when:
  def result = service.isEven(number)

  then:
  result == isEven

  where:
  number << getNumberValues()
  isEven << getIsEvenValues()
}
  

Nice error messages

then:
parseDouble(bigDecimal.doubleValue().toString()) == 1
  
Condition not satisfied:

parseDouble(bigDecimal.doubleValue().toString()) == 1
|           |          |             |           |
100.0       100        100.0         100.0       false
  

also when comparing strings

def pineapple = "pineapple"

then:
pineapple == "pipe arrays"
  
Condition not satisfied:

pineapple == "pipe arrays"
|         |
pineapple false
          7 differences (36% similarity)
          pi(n)e(-)a(pple-)
          pi(p)e( )a(rrays)
  

Nice exceptions testing

  then:
  Exception e = thrown()
  e instanceof RuntimeException
  e.message == "Could not run"
  e.cause instanceof ArithmeticException
  

@Timeout

  @Test
  @Timeout(1) //In seconds
  

There is more

http://code.google.com/p/spock/

Dziękuję!

https://github.com/amorfis/spock-pres

http://pawelstawicki.blogspot.com

@pawelstawicki

Szczecin Java Users Group

http://szczecin.jug.pl

http://devcrowd.pl/

/

#