Slide 1

Slide 1 text

7 reasons why bother learning Spock (for Java developers) Riga, 6th September 2016

Slide 2

Slide 2 text

About me Areas of expertise Automatic Testing / TDD (with Spock of course :) ) Software Craftsmanship / Code Quality Concurrency / Parallel Computing / Reactive Systems Deployment Automation / Continuous Delivery FOSS projects author and contributor, blogger, trainer CTO of small software house - Codearte targeted at clients who care about the quality Trainer in Bottega IT Solutions

Slide 3

Slide 3 text

Why bother learning Spock?

Slide 4

Slide 4 text

Reason 1 BDD specification by default

Slide 5

Slide 5 text

BDD specification by default class SimpleCalculatorSpec extends Specification { def "should sum two numbers"() { given: Calculator calculator = new SimpleCalculator() when: int result = calculator.sum(1, 2) then: result == 3 } }

Slide 6

Slide 6 text

BDD specification by default class SimpleCalculatorSpec extends Specification { def "should sum two numbers"() { given: Calculator calculator = new SimpleCalculator() when: int result = calculator.sum(1, 2) then: result == 3 } [given]/when/then (or expect) are required to compile code not just comments in code

Slide 7

Slide 7 text

Reason 2 Power Assertions

Slide 8

Slide 8 text

Power Assertions - basic case reused Java assert keyword assert (2 + 3) * 4 != (2 * 4) + (3 * 4)

Slide 9

Slide 9 text

Power Assertions - basic case reused Java assert keyword assert (2 + 3) * 4 != (2 * 4) + (3 * 4) self explaining reason of failure Assertion failed: assert (2 + 3) * 4 != (2 * 4) + (3 * 4) | | | | | | 5 20 false 8 20 12

Slide 10

Slide 10 text

Power Assertions - more complex case not only mathematical expressions String word = "Spock" int begin = 1 int end = 3 assert word.substring(begin, end) == word[begin..end]

Slide 11

Slide 11 text

Power Assertions - more complex case not only mathematical expressions String word = "Spock" int begin = 1 int end = 3 assert word.substring(begin, end) == word[begin..end] also for method return types and arguments Assertion failed: assert word.substring(begin, end) == word[begin..end] | | | | | | || | | po 1 3 | | |1 3 Spock | | poc | Spock false

Slide 12

Slide 12 text

Power Assertions - other complex case even for complicated structures and expressions assert ann.name == bob.name && ann.age == bob.age

Slide 13

Slide 13 text

Power Assertions - other complex case even for complicated structures and expressions assert ann.name == bob.name && ann.age == bob.age detailed evaluation of sub elements Assertion failed: assert ann.name == bob.name && ann.age == bob.age | | | | | | | Ann | | Bob false | | Person(name:Bob, age:7) | false Person(name:Ann, age:4)

Slide 14

Slide 14 text

Power Assertions - other complex case even for complicated structures and expressions assert ann.name == bob.name && ann.age == bob.age detailed evaluation of sub elements Assertion failed: assert ann.name == bob.name && ann.age == bob.age | | | | | | | Ann | | Bob false | | Person(name:Bob, age:7) | false Person(name:Ann, age:4) second part ignored as meaningless

Slide 15

Slide 15 text

Power Assertions self-explaining optional assert keyword in then and expect code block unless placed in Closure or separate method backported to Groovy

Slide 16

Slide 16 text

Reason 3 First class support for parameterized tests

Slide 17

Slide 17 text

Parameterized tests def "should sum two integers"() { given: Calculator calculator = new SimpleCalculator() when: int result = calculator.add(x, y) then: result == expectedResult where: x | y || expectedResult 1 | 2 || 3 -2 | 3 || 1 -1 | -2 || -3 }

Slide 18

Slide 18 text

Parameterized tests def "should sum two integers"() { given: Calculator calculator = new SimpleCalculator() when: int result = calculator.add(x, y) then: result == expectedResult where: x | y || expectedResult 1 | 2 || 3 -2 | 3 || 1 -1 | -2 || -3 } build-in support with where keyword does not stop on failure for given test case syntactic sugar for table-like data formatting

Slide 19

Slide 19 text

Parameterized tests - even better @Unroll def "should sum two integers (#x + #y = #expectedResult)"() { given: Calculator calculator = new SimpleCalculator() when: int result = calculator.add(x, y) then: result == expectedResult where: x | y || expectedResult 1 | 2 || 3 -2 | 3 || 1 -1 | -2 || -3 } separate test for every input parameters set - @Unroll visible also in reports and IDE input parameters presented in test name (with #) data pipes and data providers for advanced use cases

Slide 20

Slide 20 text

Reason 4 Built-in mocking framework

Slide 21

Slide 21 text

Simple Stubbing class DaoSpec extends Specification { def "should stub method call"() { given: Dao dao = Stub() dao.getCount() >> 1 expect: dao.getCount() == 1 } } interface Dao { int getCount() Item save(Item item) }

Slide 22

Slide 22 text

Simple Stubbing - custom logic class DaoSpec extends Specification { def "should throw exception for specific input parameters"() { given: Dao dao = Stub() dao.save(_) >> { Item item -> throw new IllegalArgumentException(item.toString()) } when: dao.save(new Item()) then: thrown(IllegalArgumentException) } } _ for any argument value arguments available inside Closure

Slide 23

Slide 23 text

Mock interactions verification class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao dao = Mock() when: dao.delete(baseItem) then: 1 * dao.delete(_) } } 1* - method called once (_) - with any value as the first parameter

Slide 24

Slide 24 text

Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao dao = Mock() when: Item returnedItem = dao.save(baseItem) then: 1 * dao.save(_) >> { Item item -> item } and: baseItem.is(returnedItem) } } has to be defined in the same statement in then section

Slide 25

Slide 25 text

Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao dao = Mock() when: Item returnedItem = dao.save(baseItem) then: 1 * dao.save(_) >> { Item item -> item } and: baseItem.is(returnedItem) } } has to be defined in the same statement in then section design suggestion: in most cases stubbing and interaction verification of the same mock shouldn't be needed

Slide 26

Slide 26 text

Reason 5 Exception testing

Slide 27

Slide 27 text

Capturing thrown exception def "should capture exception"() { when: throwNPE() then: NullPointerException e = thrown() e.message == "test NPE" }

Slide 28

Slide 28 text

Capturing thrown exception def "should capture exception"() { when: throwNPE() then: NullPointerException e = thrown() e.message == "test NPE" } thrown exception intercepted and assigned to variable for further asserting test failed if not thrown or has unexpected type Exceptions utility class to play with cause chain

Slide 29

Slide 29 text

Reason 6 Groovy magic

Slide 30

Slide 30 text

Groovy - why bother? smarter, shorten, more powerful Java Closure to make functions first-class citizen Java code (in most cases) is also valid Groovy code flat learning curve seamlessly integration with Java code can use Java libraries

Slide 31

Slide 31 text

Groovy - lists and sets compact syntax for list and set creation List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set luckyNumbers = [4, 7, 9, 7] as Set //4, 7, 9

Slide 32

Slide 32 text

Groovy - lists and sets compact syntax for list and set creation List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set luckyNumbers = [4, 7, 9, 7] as Set //4, 7, 9 accessing String secondName = names[1] //Bob String lastName = names[-1] //Scholastica

Slide 33

Slide 33 text

Groovy - lists and sets compact syntax for list and set creation List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set luckyNumbers = [4, 7, 9, 7] as Set //4, 7, 9 accessing String secondName = names[1] //Bob String lastName = names[-1] //Scholastica modification names[1] = 'Alex' //Ann, Alex, Monica, Scholastica names << 'John' //Ann, Alex, Monica, Scholastica, John Set withoutSeven = luckyNumbers - 7 //4, 9

Slide 34

Slide 34 text

Groovy - maps compact syntax for map creation Map childrenWithAge = [Ann: 5, Bob: 7, Monica: 9, Scholastica: 7]

Slide 35

Slide 35 text

Groovy - maps compact syntax for map creation Map childrenWithAge = [Ann: 5, Bob: 7, Monica: 9, Scholastica: 7] accessing childrenWithAge['Ann'] //5

Slide 36

Slide 36 text

Groovy - maps compact syntax for map creation Map childrenWithAge = [Ann: 5, Bob: 7, Monica: 9, Scholastica: 7] accessing childrenWithAge['Ann'] //5 modification childrenWithAge['Bob'] = 8 //Ann: 5, Bob: 8, Monica: 9, Scholastica: 7 Map withAlice = childrenWithAge + [Alice: 3] //Ann: 5, Bob: 8, Monica: 9, Scholastica: 7, Alice: 3

Slide 37

Slide 37 text

Functional Groovy - Closures operations on collection List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] names.findAll { String name -> name.length() > 3 } //Monica, Scholastica

Slide 38

Slide 38 text

Functional Groovy - Closures operations on collection List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] names.findAll { String name -> name.length() > 3 }.collect { String name -> //can be chained name.toUpperCase() } //MONICA, SCHOLASTICA

Slide 39

Slide 39 text

Functional Groovy - Closures operations on collection List names = ['Ann', 'Bob', 'Monica', 'Scholastica'] names.findAll { String name -> name.length() > 3 }.collect { String name -> //can be chained name.toUpperCase() } //MONICA, SCHOLASTICA it to refers Closure execution argument - in simple cases Set luckyNumbers = [4, 7, 9, 7] as Set luckyNumbers.findAll { it % 2 == 0 } //4

Slide 40

Slide 40 text

Functional Groovy - Closures inlined functional interfaces //production Java method to call void executeMultipleTimes(int number, Runnable codeToExecute); executeMultipleTimes(5, { println "Executed" }) //or simplier executeMultipleTimes(5) { println "Executed" }

Slide 41

Slide 41 text

(G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" }

Slide 42

Slide 42 text

(G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" } method execution println "Milliseconds since the epoch: ${System.currentTimeMillis()}."

Slide 43

Slide 43 text

(G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" } method execution println "Milliseconds since the epoch: ${System.currentTimeMillis()}." multi-line string String mailBody = """ Hello User, Welcome to our newsletter. Have a good day!"""

Slide 44

Slide 44 text

(G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" } method execution println "Milliseconds since the epoch: ${System.currentTimeMillis()}." multi-line string String mailBody = """ Hello User, Welcome to our newsletter. Have a good day!""".stripIndent()

Slide 45

Slide 45 text

Reason 7 Extensibility

Slide 46

Slide 46 text

Extensibility very powerful extensions mechanism dozens of internal features implemented as extensions provided out-of-box @AutoCleanup, @IgnoreIf, @RestoreSystemProperties, ... many extensions as external projects ability to reuse JUnit's @Rule and @ClassRule

Slide 47

Slide 47 text

Summary

Slide 48

Slide 48 text

Why Spock? consist and readable test code tests as specification by default all Groovy magic available to help chance to learn new language embedded mocking framework although Mockito can be used if preferred (or needed) highly extensible compatible with tools supporting JUnit

Slide 49

Slide 49 text

What's next?

Slide 50

Slide 50 text

What's next? in most cases it is worth to give Spock a try

Slide 51

Slide 51 text

What's next? in most cases it is worth to give Spock a try and fall in love with its readability and simplicity

Slide 52

Slide 52 text

What's next? in most cases it is worth to give Spock a try and fall in love with its readability and simplicity let's make a small experiment :) write all new tests in your team/project entirely in Spock for a week decide if you liked it tip: sample Spock configuration for Gradle and Maven available on my blog

Slide 53

Slide 53 text

Questions? Marcin Zajączkowski http://blog.solidsoft.info/ @SolidSoftBlog [email protected]

Slide 54

Slide 54 text

Thank you! Marcin Zajączkowski http://blog.solidsoft.info/ @SolidSoftBlog [email protected]