Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

7 reasons why bother learning Spock

467 views

Published on

JUnit has been around for over a decade. Enhanced with Mockito and AssertJ it still can be used to write decent automatic tests, however, there is a better way. Thanks to Spock Framework tests/specifications can be written faster in shorter and more readable form. During my talk I will present 7 opinionated reasons why it is worth learning Spock to bring your automatic tests to the next level. This presentation is targeted at Java developers who care about writing automatic tests (all of us?) and for some reasons haven't decided to give Spock a shot yet.

Published in: Software
  • Be the first to comment

  • Be the first to like this

7 reasons why bother learning Spock

  1. 1. 7 reasons why bother learning Spock (for Java developers) Riga, 6th September 2016
  2. 2. 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
  3. 3. Why bother learning Spock?
  4. 4. Reason 1 BDD specification by default
  5. 5. 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 } }
  6. 6. 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
  7. 7. Reason 2 Power Assertions
  8. 8. Power Assertions - basic case reused Java assert keyword assert (2 + 3) * 4 != (2 * 4) + (3 * 4)
  9. 9. 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
  10. 10. 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]
  11. 11. 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
  12. 12. Power Assertions - other complex case even for complicated structures and expressions assert ann.name == bob.name && ann.age == bob.age
  13. 13. 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)
  14. 14. 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
  15. 15. Power Assertions self-explaining optional assert keyword in then and expect code block unless placed in Closure or separate method backported to Groovy
  16. 16. Reason 3 First class support for parameterized tests
  17. 17. 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 }
  18. 18. 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
  19. 19. 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
  20. 20. Reason 4 Built-in mocking framework
  21. 21. 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) }
  22. 22. Simple Stubbing - custom logic class DaoSpec extends Specification { def "should throw exception for specific input parameters"() { given: Dao<Item> 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
  23. 23. Mock interactions verification class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao<Item> dao = Mock() when: dao.delete(baseItem) then: 1 * dao.delete(_) } } 1* - method called once (_) - with any value as the first parameter
  24. 24. Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao<Item> 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
  25. 25. Stubbing and verifying together class DaoSpec extends Specification { def "should stub and verify"() { given: Item baseItem = new Item() and: Dao<Item> 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
  26. 26. Reason 5 Exception testing
  27. 27. Capturing thrown exception def "should capture exception"() { when: throwNPE() then: NullPointerException e = thrown() e.message == "test NPE" }
  28. 28. 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
  29. 29. Reason 6 Groovy magic
  30. 30. 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
  31. 31. Groovy - lists and sets compact syntax for list and set creation List<String> names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set<Integer> luckyNumbers = [4, 7, 9, 7] as Set //4, 7, 9
  32. 32. Groovy - lists and sets compact syntax for list and set creation List<String> names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set<Integer> luckyNumbers = [4, 7, 9, 7] as Set //4, 7, 9 accessing String secondName = names[1] //Bob String lastName = names[-1] //Scholastica
  33. 33. Groovy - lists and sets compact syntax for list and set creation List<String> names = ['Ann', 'Bob', 'Monica', 'Scholastica'] Set<Integer> 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<Integer> withoutSeven = luckyNumbers - 7 //4, 9
  34. 34. Groovy - maps compact syntax for map creation Map<String, Integer> childrenWithAge = [Ann: 5, Bob: 7, Monica: 9, Scholastica: 7]
  35. 35. Groovy - maps compact syntax for map creation Map<String, Integer> childrenWithAge = [Ann: 5, Bob: 7, Monica: 9, Scholastica: 7] accessing childrenWithAge['Ann'] //5
  36. 36. Groovy - maps compact syntax for map creation Map<String, Integer> 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<String, Integer> withAlice = childrenWithAge + [Alice: 3] //Ann: 5, Bob: 8, Monica: 9, Scholastica: 7, Alice: 3
  37. 37. Functional Groovy - Closures operations on collection List<String> names = ['Ann', 'Bob', 'Monica', 'Scholastica'] names.findAll { String name -> name.length() > 3 } //Monica, Scholastica
  38. 38. Functional Groovy - Closures operations on collection List<String> names = ['Ann', 'Bob', 'Monica', 'Scholastica'] names.findAll { String name -> name.length() > 3 }.collect { String name -> //can be chained name.toUpperCase() } //MONICA, SCHOLASTICA
  39. 39. Functional Groovy - Closures operations on collection List<String> 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<Integer> luckyNumbers = [4, 7, 9, 7] as Set luckyNumbers.findAll { it % 2 == 0 } //4
  40. 40. 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" }
  41. 41. (G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" }
  42. 42. (G)Strings variable reference void printMagicNumber(int number) { println "Today magic number is $number. Congrats!" } method execution println "Milliseconds since the epoch: ${System.currentTimeMillis()}."
  43. 43. (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!"""
  44. 44. (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()
  45. 45. Reason 7 Extensibility
  46. 46. 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
  47. 47. Summary
  48. 48. 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
  49. 49. What's next?
  50. 50. What's next? in most cases it is worth to give Spock a try
  51. 51. What's next? in most cases it is worth to give Spock a try and fall in love with its readability and simplicity
  52. 52. 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
  53. 53. Questions? Marcin Zajączkowski http://blog.solidsoft.info/ @SolidSoftBlog m.zajaczkowski@gmail.com
  54. 54. Thank you! Marcin Zajączkowski http://blog.solidsoft.info/ @SolidSoftBlog m.zajaczkowski@gmail.com

×