Interesting nooks and crannies of Spock you (may) have never seen before

Interesting nooks and crannies of Spock you (may) have never seen before

I bet that Spock needs not to be introduced to anyone here. For many of us it was an entry point to the thrilling world of Groovy. Spock has a flat learning curve - a sensible Java programmer (without prior Groovy experience) can start writing Spock specifications within several minutes. However, when you combine Groovy & Spock magic, a new fascinating meta-world is created. The exploration of its every nook and cranny can take much longer (and it's definitely much more efficient with a guide ;-).

You can expect a set of more advanced (and/or less known) techniques and mechanisms, the use of which can even more simplify and facilitate the testing of our code. You have to be forewarned that we will also visit those dark areas where it is better not to enter, things that do not work or work in a way nobody expects.

Spoiler. It is a Groovy focused version of my warmly welcomed "Smarter testing Java code with Spock Framework" presentation, enriched with additional tricks and traps.

220d0825b07706221aeae4751057ede8?s=128

Marcin Zajączkowski

June 02, 2016
Tweet

Transcript

  1. None
  2. None
  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification

    { //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... }
  17. import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification

    { //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... } MissingMethodException
  18. import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable @Requires({ isUrlAvailable("http://some-host.intranet/") }) class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification

    { @Memoized //could be also from trait or other class static boolean isUrlAvailable(String url) { //... } def "should do one thing in intranet"() { ... } def "should do another thing in intranet"() { ... } ... } @Memoized
  19. @Requires({ System.getProperty("os.arch") == "amd64" }) //old way def "should use

    optimization on 64-bit systems"() { ... } @Requires({ System.getenv().containsKey("ENABLE_CRM_INTEGRATION_TESTS") }) //old way def "should do complicated things with CRM"() { ... }
  20. @Requires({ System.getProperty("os.arch") == "amd64" }) //old way def "should use

    optimization on 64-bit systems"() { ... } @Requires({ System.getenv().containsKey("ENABLE_CRM_INTEGRATION_TESTS") }) //old way def "should do complicated things with CRM"() { ... }
  21. spock.util.environment.OperatingSystem os.name os.version String getName() //Linux String getVersion() //8.1 Family

    getFamily() //SOLARIS (enum) boolean isLinux() boolean isWindows() boolean isMacOs() boolean isSolaris() boolean isOther() @Requires({ os.linux || os.macOs }) def "should use fancy console features"() { ... }
  22. spock.util.environment.Jvm java.version java.specification.version boolean isJava7() //true only if Java 7

    boolean isJava8Compatible() //true if Java 8+ String getJavaVersion() //e.g. "1.8.0_05" String getJavaSpecificationVersion() //e.g. "1.8" @IgnoreIf({ !jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... }
  23. spock.util.environment.Jvm java.version java.specification.version boolean isJava7() //true only if Java 7

    boolean isJava8Compatible() //true if Java 8+ String getJavaVersion() //e.g. "1.8.0_05" String getJavaSpecificationVersion() //e.g. "1.8" @IgnoreIf({ !jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... }
  24. spock.util.environment.Jvm java.version java.specification.version boolean isJava7() //true only if Java 7

    boolean isJava8Compatible() //true if Java 8+ String getJavaVersion() //e.g. "1.8.0_05" String getJavaSpecificationVersion() //e.g. "1.8" @IgnoreIf({ !jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... }
  25. //from trait or util class private static final PreconditionContext XXX

    = new PreconditionContext() @IgnoreIf({ !XXX.jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... } @Requires({ XXX.os.linux || XXX.os.macOs }) def "should use fancy console features"() { ... } PreconditionContext
  26. //from trait or util class private static final PreconditionContext XXX

    = new PreconditionContext() @IgnoreIf({ !XXX.jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... } @Requires({ XXX.os.linux || XXX.os.macOs }) def "should use fancy console features"() { ... } PreconditionContext
  27. //from trait or util class private static final PreconditionContext XXX

    = new PreconditionContext() @IgnoreIf({ !XXX.jvm.java8Compatible }) def "should return empty Optional by default for unstubbed methods with Java 8+"() { ... } @Requires({ XXX.os.linux || XXX.os.macOs }) def "should use fancy console features"() { ... } PreconditionContext
  28. None
  29. setup/cleanup

  30. setup/cleanup def "complex logic should work with fixed thread pool"()

    { setup: //alias for 'given:' ExecutorService threadPool = Executors.newFixedThreadPool(1) when: String returnedValue = threadPool .submit({ "val" } as Callable) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() } where
  31. setup/cleanup def "complex logic should work with fixed thread pool"()

    { setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) when: String returnedValue = threadPool .submit({ "val" } as Callable) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() }
  32. def "complex logic should work with fixed thread pool"() {

    setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = { "val" } as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() }
  33. def "complex logic should work with fixed thread pool"() {

    setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = { "val" } as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) //null is returned! then: returnedValue == "val" cleanup: threadPool?.shutdown() } Runnable Callable
  34. def "complex logic should work with fixed thread pool"() {

    setup: ExecutorService threadPool = Executors.newFixedThreadPool(1) and: Callable task = [call: { "val" }] as Callable when: String returnedValue = threadPool .submit(task) .get(1, TimeUnit.SECONDS) then: returnedValue == "val" cleanup: threadPool?.shutdown() }
  35. class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection def

    beforeSpec() { dbConnection = new H2DBConnection(...) } def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close() }
  36. class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection =

    new H2DBConnection(...) def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); }
  37. class FancyDBSpec extends Specification { @Shared private DBConnection dbConnection =

    new H2DBConnection(...) def cleanupSpec() { dbConnection.close() } def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); }
  38. class FancyDBSpec extends Specification { @Shared @AutoCleanup private DBConnection dbConnection

    = new H2DBConnection(...) def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void close(); } close()
  39. class FancyDBSpec extends Specification { @Shared @AutoCleanup("release") private DBConnection dbConnection

    = new H2DBConnection(...) def "should do fancy things on DB"() { ... } def "should do fancy2 things on DB"() { ... } } interface DBConnection { ... void release(); }
  40. cleaup cleanupSpec close() @AutoCleanup("release") @AutoCleanup(quiet = true)

  41. @Before @After setupSpec() setup() cleanup() cleanupSpec() @BeforeClass @Before @After @AfterClass

  42. +---------------+ | Specification | +---------------+

  43. +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec

    | +---------------------+
  44. +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec

    | +---------------------+ ^ | +------------------+ | BaseDatabaseSpec | +------------------+
  45. +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec

    | +---------------------+ ^ | +------------------+ | BaseDatabaseSpec | +------------------+ ^ ^ | | | | +-------- + +---------+ | DbSpec1 | | DbSpec2 | +---------+ +---------+
  46. +---------------+ | Specification | +---------------+ ^ | +---------------------+ | BaseIntegrationSpec

    | +---------------------+ ^ ^ | | +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+
  47. +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- +

    | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------+ ^ ^ | | +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+
  48. +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- +

    | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------------+ ^ ^ | BaseWiremockWithActiveMqSpec ??? | | | +----------------------------------+ +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+
  49. +---------------+ | Specification | +---------------+ ^ | +---------------------+ +--------------------------- +

    | BaseIntegrationSpec |<-------| BaseWiremockWithDbSpec ??? | +---------------------+ +----------------------------------+ ^ ^ | BaseWiremockWithActiveMqSpec ??? | | | +----------------------------------+ +------------------+ +----------------- + | BaseDatabaseSpec | | BaseWiremockSpec | +------------------+ +------------------+ ^ ^ ^ ^ | | | | | | | | +-------- + +---------+ +---------------+ +---------------+ | DbSpec1 | | DbSpec2 | | WiremockSpec1 | | WiremockSpec2 | +---------+ +---------+ +---------------+ +---------------+
  50. None
  51. trait DatabaseTrait { static DBConnection dbConnection def setupSpec() { //or

    @BeforeClass or inline //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } }
  52. trait DatabaseTrait { static DBConnection dbConnection def setupSpec() { //or

    @BeforeClass or inline //prepare DB } def cleanupSpec() { //or @AfterClass //clean up DB } } //no more infrastructure code needed in specifications class Fancy1DatabaseSpec extends Specification implements DatabaseTrait { def "should do fancy1 thing on database"() { //fancy1 things on database } } class Fancy2DatabaseSpec extends Specification implements DatabaseTrait { def "should do fancy2 thing on database"() { //fancy2 things on database } }
  53. trait DatabaseTrait { ... } trait ActiveMQTrait { ... }

    trait WireMockTrait { ... } class VerySpecificIntegrationSpec extends Specification implements DatabaseTrait, WireMockTrait { def "should do very specific integration things"() { ... } }
  54. @AutoCleanup cleanup(Spec) @Shared static static

  55. None
  56. @Unroll def "should validate PESEL (#pesel) correctness (#isValid)"() { expect:

    sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } where
  57. @Unroll def "should validate PESEL (#pesel) correctness (#isValid)"() { expect:

    sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true } #pesel #isValid
  58. @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"()

    { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true description = isValid ? "valid" : "invalid" }
  59. @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"()

    { expect: sut.validate(pesel) == isValid where: pesel || isValid "123" || false "abcd" || false "97110208631" || true description = isValid ? "valid" : "invalid" }
  60. @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"()

    { expect: sut.validate(pesel) == isValid where: pesel << ["123", "abcd", "97110208631"] isValid << [false, false, true] description = isValid ? "valid" : "invalid" } <<
  61. @Unroll("#pesel is valid (#dbId)") def "should validate PESEL correctness (CSV)"()

    { expect: sut.validate(pesel) where: [dbId, _, _, pesel] << readValidPeopleFromCSVFile() .readLines().collect { it.split(',') } //ugly way to read CSV - don't do this }
  62. @Unroll

  63. @Unroll @Unroll @Unroll class PeselValidatorSpec extends Specification { def "should

    pass valid PESEL #number"() { ... } def "should reject too short PESEL #number"() { ... } def "should pass Mr. President PESEL"() { ... } (...) }
  64. //dependency in Gradle/Maven/Ant testCompile 'info.solidsoft.spock:spock-global-unroll:0.5.1' @Unroll class PeselValidatorSpec extends Specification

    { //unrolled automatically without @Unroll def "should pass valid PESEL #number"() { ... } (...) }
  65. @Roll @DisableGlobalUnroll @Roll class PeselValidatorSpec extends Specification { //one big

    test for multiple input parameters def "should not be unrolled for some reasons PESEL #number"() { ... } (...) }
  66. @Unroll @Roll @Roll class PeselValidatorSpec extends Specification { //one big

    test for multiple input parameters def "should not be unrolled for some reasons PESEL #number"() { ... } //unrolled anyway @Unroll("PESEL '#pesel' should be #description") def "should validate PESEL correctness"() { ... } (...) }
  67. None
  68. class DaoSpec extends Specification { def "should stub consecutive calls"

    () { given: Dao<Item> dao = Stub(Dao) dao.getCount() >> 1 >> 2 expect: dao.getCount() == 1 and: dao.getCount() == 2 and: dao.getCount() == 2 } }
  69. class DaoSpec extends Specification { def "should stub consecutive calls"

    () { given: Dao<Item> dao = Stub(Dao) dao.getCount() >>> [1, 2] expect: dao.getCount() == 1 and: dao.getCount() == 2 and: dao.getCount() == 2 } }
  70. class DaoSpec extends Specification { def "should stub consecutive calls

    (exception)"() { given: Dao<Item> dao = Stub(Dao) dao.getCount() >> 1 >> { throw new IllegalArgumentException() } >> 3 expect: dao.getCount() == 1 when: dao.getCount() then: thrown(IllegalArgumentException) expect: dao.getCount() == 3 }
  71. class DaoSpec extends Specification { def "should stub consecutive calls

    (exception)"() { given: Dao<Item> dao = Stub(Dao) dao.getCount() >> 1 >> { throw new IllegalArgumentException() } >> 3 expect: dao.getCount() == 1 when: dao.getCount() then: thrown(IllegalArgumentException) expect: dao.getCount() == 3 }
  72. class DaoSpec extends Specification { def "should stub consecutive calls

    (exception)"() { given: Dao<Item> dao = Stub(Dao) dao.getCount() >> 1 >> { throw new IllegalArgumentException(???) } >> 3 expect: dao.getCount() == 1 when: dao.getCount() then: thrown(IllegalArgumentException) expect: dao.getCount() == 3 }
  73. class Dao<T> { T save(T element) } class DaoSpec extends

    Specification { def "should stub method call to return input value"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub() dao.save(_) >> { it[0] } when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } }
  74. class DaoSpec extends Specification { def "should demonstrate common error

    with one parameter"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub() dao.save(_) >> { it } //broken - 'it' is an array - it[0] when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } }
  75. class DaoSpec extends Specification { def "should stub method call

    to return input value"() { given: Item baseItem = new Item() and: Dao<Item> dao = Stub() dao.save(_) >> { Item item -> item } when: Item returnedItem = dao.save(baseItem) then: baseItem.is(returnedItem) } }
  76. class DaoSpec extends Specification { def "should stub and verify"()

    { given: Item baseItem = new Item() and: Dao<Item> dao = Mock() dao.save(_) >> { Item item -> item } when: Item returnedItem = dao.save(baseItem) then: 1 * dao.save(_) and: baseItem.is(returnedItem) } }
  77. class DaoSpec extends Specification { def "should stub and verify"()

    { given: Item baseItem = new Item() and: Dao<Item> dao = Mock() dao.save(_) >> { Item item -> item } when: Item returnedItem = dao.save(baseItem) then: 1 * dao.save(_) and: baseItem.is(returnedItem) } }
  78. 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) } } then
  79. @Ignore("broken") def "notification should be send after business operation (ignore

    order)"() { given: Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) //order does not matter 1 * notifier.sendNotification(_) }
  80. def "notification should be send after business operation"() { given:

    Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) then: 1 * notifier.sendNotification(_) }
  81. @Ignore("broken") def "notification should be send after business operation (with

    'and')"() { given: Dao dao = Mock() Notifier notifier = Mock() and: def sut = new BusinessService(dao, notifier) when: sut.processOrder() then: 1 * dao.saveOrder(_) and: //'and' does not verify execution order 1 * notifier.sendNotification(_) } and
  82. None
  83. 1 * tacticalStation.fireTorpedo(2) //equal to 2 1 * tacticalStation.fireTorpedo(!2) //not

    equal to 2 1 * tacticalStation.fireTorpedo(_) //any argument 1 * scienceStation.analyzeInput(!null) //not null argument 1 * scienceStation.analyzeInput(_ as String) //instance of String 1 * tacticalStation.fireTorpedo({ it > 2 }) //any argument greater than 2 1 * ts.smellyFindNumberOfShipsInRangeByCriteria(_, { it.contains("fiant") }, 4)
  84. 1 * tacticalStation.fireTorpedo(2) //equal to 2 1 * tacticalStation.fireTorpedo(!2) //not

    equal to 2 1 * tacticalStation.fireTorpedo(_) //any argument 1 * scienceStation.analyzeInput(!null) //not null argument 1 * scienceStation.analyzeInput(_ as String) //instance of String 1 * tacticalStation.fireTorpedo({ it > 2 }) //any argument greater than 2 1 * ts.smellyFindNumberOfShipsInRangeByCriteria(_, { it.contains("fiant") }, 4) ts.smellyFindNumberOfShipsInRangeByCriteria(_, { it.contains("fiant") }, 4) >> 2
  85. None
  86. def dao = Mock(Dao) //Groovy way

  87. def dao = Mock(Dao) //Groovy way Dao dao = Mock(Dao)

    //Java way
  88. def dao = Mock(Dao) //Groovy way Dao dao = Mock(Dao)

    //Java way Dao dao = Mock() //shorter Java way?
  89. def dao = Mock(Dao) //Groovy way Dao dao = Mock(Dao)

    //Java way Dao dao = Mock() //shorter Java way
  90. def dao = Mock(Dao) //Groovy way Dao dao = Mock(Dao)

    //Java way Dao dao = Mock() //shorter Java way
  91. None
  92. None
  93. None
  94. interface Dao<T> { Optional<T> getMaybeById(long id) ... } def "should

    not fail on unstubbed call with Optional return type"() { given: Dao<Order> dao = Stub() when: dao.getMaybeById(5) then: noExceptionThrown() }
  95. interface Dao<T> { Optional<T> getMaybeById(long id) ... } @Ignore("Broken -

    Optional is final") def "should not fail on unstubbed call with Optional return type"() { given: Dao<Order> dao = Stub() when: dao.getMaybeById(5) then: noExceptionThrown() } CannotCreateMockException: Cannot create mock for class java.util.Optional because Java mocks cannot mock final classes.
  96. EmptyOrDummyResponse

  97. EmptyOrDummyResponse null ZeroOrNullResponse

  98. EmptyOrDummyResponse null ZeroOrNullResponse def "should not fail on unstubbed call

    with Optional return type - workaround 1"() { given: Dao<Order> dao = Stub([defaultResponse: ZeroOrNullResponse.INSTANCE]) when: dao.getMaybeById(5) then: noExceptionThrown() }
  99. EmptyOrDummyResponse null ZeroOrNullResponse

  100. EmptyOrDummyResponse null ZeroOrNullResponse def "should not fail on unstubbed call

    with Optional return type - workaround 2"() { given: Dao<Order> dao = Mock() //instead of Stub() when: dao.getMaybeById(5) then: noExceptionThrown() }
  101. EmptyOrDummyResponse null ZeroOrNullResponse def "should not fail on unstubbed call

    with Optional return type - workaround 2"() { given: Dao<Order> dao = Mock() //instead of Stub() when: dao.getMaybeById(5) then: noExceptionThrown() } Optional
  102. None
  103. class Java8EmptyOrDummyResponse implements IDefaultResponse { public static final Java8EmptyOrDummyResponse INSTANCE

    = new Java8EmptyOrDummyResponse(); private Java8EmptyOrDummyResponse() {} @Override public Object respond(IMockInvocation invocation) { if (invocation.getMethod().getReturnType() == Optional) { return Optional.empty() } //possibly CompletableFutures.completedFuture(), dates and maybe others return EmptyOrDummyResponse.INSTANCE.respond(invocation) } }
  104. defaultResponse Stub def "should return empty Optional for unstubbed calls"()

    { given: Dao<Order> dao = Stub([defaultResponse: Java8EmptyOrDummyResponse.INSTANCE]) when: Optional<Order> result = dao.getMaybeById(5) then: result?.isPresent() == false //NOT the same as !result?.isPresent() }
  105. defaultResponse Stub def "should return empty Optional for unstubbed calls"()

    { given: Dao<Order> dao = Stub([defaultResponse: Java8EmptyOrDummyResponse.INSTANCE]) when: Optional<Order> result = dao.getMaybeById(5) then: result?.isPresent() == false //NOT the same as !result?.isPresent() } Dao<Order> dao = Stub8(Dao) <T> T Stub8(Class<T> clazz) { return Stub([defaultResponse: Java8EmptyOrDummyResponse.INSTANCE], clazz) }
  106. def "should change real method execution return value"() { given:

    TacticalStation tsSpy = Spy() and: tsSpy.getTubeStatus(_) >> { if (it[0] == 4) { return TubeStatus.BROKEN } else { return callRealMethod() } } expect: tsSpy.getTubeStatus(1) == TubeStatus.LOADED and: tsSpy.getTubeStatus(4) == TubeStatus.BROKEN } callRealMethod()
  107. def "should call real method with changed arguments"() { given:

    TacticalStation tsSpy = Spy() and: tsSpy.getTubeStatus(_) >> { int tubeNumber -> return callRealMethodWithArgs(tubeNumber + 1) } expect: ... } callRealMethodWithArgs()
  108. None
  109. Assertion failed: assert word.substring(begin, end) == word[begin..end] | | |

    | | | || | | po 1 3 | | |1 3 Spock | | poc | Spock false assert then expect
  110. Assertion failed: assert word.substring(begin, end) == word[begin..end] | | |

    | | | || | | po 1 3 | | |1 3 Spock | | poc | Spock false assert then expect
  111. assert when: ... then: assertCompatibilityWithEU49683Directive(order) } private void assertCompatibilityWithEU49683Directive(Order order)

    { assert order.field1 == ... assert order.field2 == ... }
  112. assert when: ... then: assertCompatibilityWithEU49683Directive(order) } private void assertCompatibilityWithEU49683Directive(Order order)

    { assert order.field1 == ... assert order.field2 == ... } given given: int initialNumberOfFiles = getNumberOfFileInDirectory() assert initialNumberOfFiles == 0 when: ...
  113. assert expect: GParsPool.withPool { ["1"].eachParallel CorrelationIdUpdater.wrapClosureWithId { assert it ==

    "1" } }
  114. assert expect: GParsPool.withPool { ["1"].eachParallel CorrelationIdUpdater.wrapClosureWithId { assert it ==

    "1" } }
  115. def "should show nice error message on null dereference"(){ when:

    Person readPerson = dao.getById(TEST_WOMAN) then: readPerson.sex == Sex.WOMAN ... }
  116. def "should show nice error message on null dereference"(){ when:

    Person readPerson = dao.getById(TEST_WOMAN) then: readPerson.sex == Sex.WOMAN ... } java.lang.NullPointerException: Cannot get property 'sex' on null object ?.
  117. def "should show nice error message on null dereference"(){ when:

    Person readPerson = dao.getById(TEST_WOMAN) then: readPerson?.sex == Sex.WOMAN ... } readPerson?.sex == Sex.WOMAN | | | null | false null
  118. def "should show nice error message on null dereference"(){ when:

    Person readPerson = dao.getById(TEST_WOMAN) then: readPerson.sex == Sex.WOMAN //wihtout '?.' ... } java.lang.NullPointerException: Cannot invoke method startsWith() on null object readPerson.sex == Sex.WOMAN | | | null | false java.lang.NullPointerException: Cannot get property 'sex' on null object
  119. @Stepwise

  120. None
  121. None
  122. None
  123. None
  124. None
  125. None
  126. None