SlideShare a Scribd company logo
1 of 118
Spock
Zan Thrash
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             Book book = new Book(isdn:book & a library"
             given: "I have a checked out '1234567ABC').save()
             Library library = new Library(name: 'Carnegie').save()
             controller.catalogService = new CatalogService()
             params.id = library.id for controller"
             and: "all dependancies
             params.isdn = book.isdn
             and: "we submit the isdn and library id"
             controller.returnBook()
             JSONElement json = response.getJson()

           assertEquals( json.isdn, book.isdn )
           when: "submit the form"
           assertEquals( json.status, 'FILED' )
     }
}          then: "verify the returned book is FILED"
               json.isdn == book.isdn
               json.status == 'FILED'
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             Book book = new Book(isdn:book & a library"
             given: "I have a checked out '1234567ABC').save()
             Library library = new Library(name: 'Carnegie').save()
             controller.catalogService = new CatalogService()
             params.id = library.id for controller"
             and: "all dependancies
             params.isdn = book.isdn
             and: "we submit the isdn and library id"
             controller.returnBook()
             JSONElement json = response.getJson()

           assertEquals( json.isdn, book.isdn )
           when: "submit the form"
           assertEquals( json.status, 'FILED' )
     }
}          then: "verify the returned book is FILED"
               json.isdn == book.isdn
               json.status == 'FILED'
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             Book book = new Book(isdn:book & a library"
             given: "I have a checked out '1234567ABC').save()
             Library library = new Library(name: 'Carnegie').save()
             controller.catalogService = new CatalogService()
             params.id = library.id for controller"
             and: "all dependancies
             params.isdn = book.isdn
             and: "we submit the isdn and library id"
             controller.returnBook()
             JSONElement json = response.getJson()

           assertEquals( json.isdn, book.isdn )
           when: "submit the form"
           assertEquals( json.status, 'FILED' )
     }
}          then: "verify the returned book is FILED"
               json.isdn == book.isdn
               json.status == 'FILED'
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
             controller.catalogService = new CatalogService()
             params.id = library.id
             and: "we submit the isdn and library id"
             params.isdn = book.isdn
    
             controller.returnBook()
             JSONElement json = response.getJson()
             when: "submit the form"
        
             assertEquals( json.isdn, book.isdn )
             assertEquals( json.status, 'FILED' )
             then: "verify the returned book is FILED"
       }         json.isdn == book.isdn
                 json.status == 'FILED'
}
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
                 controller.catalogService = new CatalogService()
             params.id = library.id
             and: "we submit the isdn and library id"
             params.isdn = book.isdn
    
             controller.returnBook()
             JSONElement json = response.getJson()
             when: "submit the form"
        
             assertEquals( json.isdn, book.isdn )
             assertEquals( json.status, 'FILED' )
             then: "verify the returned book is FILED"
       }         json.isdn == book.isdn
                 json.status == 'FILED'
}
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
                 controller.catalogService = new CatalogService()
             and: "we submit the isdn and library id"
                 params.id = library.id
                 params.isdn = book.isdn

           when: "submit the form"
           controller.returnBook()
           JSONElement json = response.getJson()
           then: "verify the returned book is FILED"
               json.isdn == book.isdn
           assertEquals( json.isdn, book.isdn )
           assertEquals( json.status, 'FILED' )
               json.status == 'FILED'
       }
}
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
                 controller.catalogService = new CatalogService()
             and: "we submit the isdn and library id"
                 params.id = library.id
                 params.isdn = book.isdn

           when: "submit the form"
              controller.returnBook()
              JSONElement json = response.getJson()
           then: "verify the returned book is FILED"
               json.isdn == book.isdn
           assertEquals( json.isdn, book.isdn )
           assertEquals( json.status, 'FILED' )
               json.status == 'FILED'
       }
}
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
                 controller.catalogService = new CatalogService()
             and: "we submit the isdn and library id"
                 params.id = library.id
                 params.isdn = book.isdn

           when: "submit the form"
              controller.returnBook()
              JSONElement json = response.getJson()
           then: "verify the returned book is FILED"
               json.isdn == book.isdn book.isdn )
               assertEquals( json.isdn,
               json.status == 'FILED'
               assertEquals( json.status, 'FILED' )
       }
}
@TestFor(LibraryController)
@Mock([Book, Library])

class LibraryControllerTests extends Specification{
         LibraryControllerSpec {
     void testReturnBookToLibrary() { libraray"() {
             "sucessfully return book to
             given: "I have a checked out book & a library"
                 Book book = new Book(isdn: '1234567ABC').save()
                 Library library = new Library(name: 'Carnegie').save()
             and: "all dependancies for controller"
                 controller.catalogService = new CatalogService()
             and: "we submit the isdn and library id"
                 params.id = library.id
                 params.isdn = book.isdn

           when: "submit the form"
              controller.returnBook()
              JSONElement json = response.getJson()
           then: "verify the returned book is FILED"
               json.isdn == book.isdn book.isdn )
               assertEquals( json.isdn,
               json.status == 'FILED'
               assertEquals( json.status, 'FILED' )
       }
}
Fixture Methods
def setup() {}
def cleanup() {}
def setupSpec() {}
def cleanupSpec() {}
void "add 2 things together"() {
    expect:
        1 + 2 == 3
           'Bat' + 'man' == 'Bat man'
        99 + 1 == 100
}
Condition not satisfied:

'Bat' + 'man' == 'Bat man'
      |       |
      Batman  false
              1 difference (85% similarity)
              Bat(-)man
              Bat( )man
Condition not satisfied:

json.status == 'FILE'
|    |      |
|    FILED  false
|           1 difference (80% similarity)
|           FILE(D)
|           FILE(-)
[id:1, author:null, title:UNKNOWN, status:FILED, class:opi.com.Book, isdn:1234567ABC
void "add 2 things together"() {
    expect:
        1 + 2 == 3
           'Bat' + 'man' == 'Bat man'
        99 + 1 == 100
}
void "add 2 things together"() {
    expect:"a + b = c"
        a + b == c
    where:
        a << [1, 'Bat', 99]
        b << [1, 'man', 1]
        c << [2, 'Batman', '100']
}
@Unroll
void "add 2 things together"() {
    expect:"a + b = c"
        a + b == c
    where:
        a << [1, 'Bat', 99]
        b << [1, 'man', 1]
        c << [2, 'Batman', '100']
}
@Unroll('#a + #b = #c')
void "add 2 things together"() {
    expect:"a + b = c"
        a + b == c
    where:
        a << [1, 'Bat', 99]
        b << [1, 'man', 1]
        c << [2, 'Batman', '100']
}
@Unroll
void "#a + #b = #c"() {
    expect:"a + b = c"
        a + b == c
    where:
        a << [1, 'Bat', 99]
        b << [1, 'man', 1]
        c << [2, 'Batman', '100']
}
@Unroll('#a + #b = #c')
void "add 2 things together"() {
    expect:"a + b = c"
        a + b == c
    where:
        a      | b     || c
        1      | 1     || 2
        99     | 1     || 100
        "Bat" | "man" || "Batman"

}
void "custom data provider" () {
    expect:"a + b = c"
        a + b == c

    where:
        [a, b, c] << new ABCDataProvider()
}
class ABCDataProvider implements Iterator{

    List list = [[1,2,3], [1,99, 100], ['Bat', 'man', 'Batman']]
    int position = 0

    boolean hasNext() {
        position < list.size()
    }

      Object next() {
          def result = list[position]
          position += 1
          result
      }

    def size() {
        list.size()
    }

    def close(){ }

    void remove() { }
}
Interactions
def catalogService = Mock(CatalogService)


CatalogService catalogService = Mock()
def catalogService = Mock(CatalogService)


CatalogService catalogService = Mock()
void "test interaction scoping"() {
    given:"create mock CatalogService"
        CatalogService service = Mock()
        controller.catalogService = service
    and: "make sure it can file books"
        service.isAvailable(_ as Book) >> true

    when:"we verify isdn"
        params.isdn = book.isdn
        def result = controller.verifyISDN()

    then:"verify the book was filed"
        1 * service.inquired(_)
        result == true
}
void "test interaction scoping"() {
    given:"create mock CatalogService"
        CatalogService service = Mock()
        controller.catalogService = service
    and: "make sure it can file books"
        service.isAvailable(_ as Book) >> true

    when:"we verify isdn"                        Global
        params.isdn = book.isdn
        def result = controller.verifyISDN()

    then:"verify the book was filed"
        1 * service.inquired(_)
        result == true
}
void "test interaction scoping"() {
    given:"create mock CatalogService"
        CatalogService service = Mock()
        controller.catalogService = service
    and: "make sure it can file books"
        service.isAvailable(_ as Book) >> true

    when:"we verify isdn"                        Local
        params.isdn = book.isdn
        def result = controller.verifyISDN()

    then:"verify the book was filed"
        1 * service.inquired(_)
        result == true
}
void "test interaction scoping"() {
    given:"create mock CatalogService"
        CatalogService service = Mock()
        controller.catalogService = service
    and: "make sure it can file books"
        service.isAvailable(_ as Book) >> true

    when:"we verify isdn"                        Required
        params.isdn = book.isdn
        def result = controller.verifyISDN()

    then:"verify the book was filed"
        1 * service.inquired(_)
        result == true
}
void "test interaction scoping"() {
    given:"create mock CatalogService"
        CatalogService service = Mock()
        controller.catalogService = service
    and: "make sure it can file books"
        service.isAvailable(_ as Book) >> true

    when:"we verify isdn"                        Optional
        params.isdn = book.isdn
        def result = controller.verifyISDN()

    then:"verify the book was filed"
        1 * service.inquired(_)
        result == true
}
1 * catalogService.file(book, library) >> 'FILED'


  Carnality
1 * catalogService.file(book, library) >> 'FILED'


  Carnality
1 * catalogService.file(book, library) >> 'FILED'


  Carnality
(0..3) * catalogService.file(book, library) >> 'FILED'


        Carnality
(3.._) * catalogService.file(book, library) >> 'FILED'


        Carnality
(_..3) * catalogService.file(book, library) >> 'FILED'


        Carnality
TooFewInvocationsError
TooManyInvocationsError
(_..3) * catalogService.file(book, library) >> 'FILED'


        Carnality Target Constraint
(_..3) * catalogService.file(book, library) >> 'FILED'


        Carnality Target Constraint
(_..3) * _.file(book, library) >> 'FILED'


             Target Constraint
(_..3) * _.file(book, library) >> 'FILED'


             Target Constraint
(_..3) * catalogService.file(book, library) >> 'FILED'


                   Target Constraint Conststraint
                              Method
(_..3) * catalogService.file(book, library) >> 'FILED'


                   Target Constraint Conststraint
                              Method
(_..3) * catalogService./f.*/(book, library) >> 'FILED'


                                Method Conststraint
(_..3) * catalogService./f.*/(book, library) >> 'FILED'


                                Method Conststraint
(_..3) * catalogService._(book, library) >> 'FILED'


                               Method Conststraint
(_..3) * catalogService._(book, library) >> 'FILED'


                               Method Conststraint
(_..3) * catalogService.file(book, library) >> 'FILED'


                               Method Conststraint List Constraint
                                         Argument
(_..3) * catalogService.file(book, library) >> 'FILED'


                               Method Conststraint List Constraint
                                         Argument
(_..3) * catalogService.file(_, _) >> 'FILED'


                                         Argument List Constraint
(_..3) * catalogService.file(*_) >> 'FILED'


                                      Argument List Constraint
(_..3) * catalogService.file(_ as Book, _) >> 'FILED'


                                           Argument List Constraint
(_..3) * catalogService.file(_ as Book, !null) >> 'FILED'


                                           Argument List Constraint
(_..3) * catalogService.file({it.isdn > 1}, !null) >> 'FILED'


                                            Argument List Constraint
1 * catalogService.file(book, library) >> 'FILED'


                                    Return Values Constraint
                                    Argument List
1 * catalogService.file(book, library) >> 'FILED'


                                    Return Values Constraint
                                    Argument List
3 * catalogService.file(book, library) >>> ['FILED',‘ERROR’]


                                    Return Values
3 * catalogService.file(book, library) >> {
                                  book.status = FILED
                                  return book
                                 }



                                   Return Values
3 * catalogService.file(book, library) >> {
                        throw new TimeoutException()
                     }



                                   Return Values
foo.bar() >> { throw new IOException() } >>> [1, 2, 3] >> { throw new RuntimeException() } 




                                                  Return Values
(_.._) * _._(*_)
Order Is NOT enforced

 void "test ordered interactions"() {
   ...

       then:"verify the book was filed"
           1 * service.inquired(_)
           2 * service.notifyLibrarian(*_)
           result == true
 }
Interactions are NOT ordered

void "test ordered interactions"() {
  ...

    then:"verify an inquiry was add to the book"
        1 * service.inquired(_)
      then:"verify that all librarians are notified "
        2 * service.notifyLibrarian(*_)
        result == true
}
Extensions
@Unroll
@Shared
@Timeout()
     @Timeout(10)
@Timeout(value=10, unit=TimeUnit.MILLISECONDS)
@Stepwise
@Ignore
@IgnoreRest
@IgnoreIf({ 1 })
@FailsWith(IOException)
@AutoCleanup()
@AutoCleanup(‘dispose’)
@AutoCleanup(quite=true)
@Use([Category])
class Dynamic {}
void "test the Use annotation"() {
    when:"call awesomeness"
        def result = new Dynamic().awesomeness('very')
    then:"check result"
        result == 'so very awesome'

}
class DynamicCategory {
    
    static String awesomeness(Dynamic dynamic, String foo) {
        “so $foo awesome”
    }
    
}
@Use(DynamicCategory)
void "test the Use annotation"() {
    when:"call awesomeness"
        def result = new Dynamic().awesomeness('very')
    then:"check result"
        result == 'so very awesome'

}
@Use(DynamicCategory)
void "test the Use annotation"() {
    when:"call awesomeness"
        def result = new Dynamic().awesomeness('very')
    then:"check result"
        result == 'so very awesome'

}
void "test the Use annotation"() {
    when:"call awesomeness"
             use(DynamicCategory){
            def result = new Dynamic().awesomeness('very')
             }
    then:"check result"
        result == 'so very awesome'

}
@ConfineMetaClassChanges([Class])
~/.spock/SpockConfig.groovy
import spock.util.mop.Use
import spock.lang.*
import grails.plugin.spock.IntegrationSpec

runner {
    optimizeRunOrder true
    filterStackTrace false

    include Unroll, Use

    exclude {
        baseClass IntegrationSpec
    }

}
>$ grails -Dspock.configuration=test/SpockConfig.groovy test-app
Rolling your own
Spock Extensions
import spock.lang.*

@SayOnFail
class MyFirstSpec extends Specification {

  @SayOnFail(value="Danger! Danger!", voice='Zarvox')
  def "let's try this!"() {
    expect:
    Math.max(1, 2) == 3
  }
}
Step 1: Create Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@ExtensionAnnotation(SayOnFailExtension)

public @interface SayOnFail {
    String value() default ''
    String voice() default "Alex"
}
Step 1: Create Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@ExtensionAnnotation(SayOnFailExtension)

public @interface SayOnFail {        Retention
    String value() default ''
    String voice() default "Alex"
}
Step 1: Create Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@ExtensionAnnotation(SayOnFailExtension)

public @interface SayOnFail {         Target
    String value() default ''
    String voice() default "Alex"
}
Step 1: Create Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@ExtensionAnnotation(SayOnFailExtension)

public @interface SayOnFail {        Extension
    String value() default ''
    String voice() default "Alex"
}
Step 1: Create Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@ExtensionAnnotation(SayOnFailExtension)
                                    Default Values
public @interface SayOnFail {
    String value() default ''
    String voice() default "Alex"
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }

      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }                                                                          Extend
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }                                                                 Override for spec
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }                                                           Add interceptor to all
      }
                                                                            methods
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }                                                              Override for feature
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }                                                                Create Interceptor
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 2: Create Extension
class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{

      @Override
      void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {
          spec.features.each {FeatureInfo feature ->
              if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {
                  visitFeatureAnnotation(sayOnError, feature)
              }
          }
      }                                                                  Add to Feature
      @Override
      void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {
          def interceptor = new SayOnFailInterceptor(sayOnError, feature)
          feature.getFeatureMethod().addInterceptor(interceptor)
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }

      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                   Implement Interface
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                              Override
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                          run method
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                           if test fails
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                        do your thing
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
Step 3: Create Intercepter
class SayOnFailInterceptor implements IMethodInterceptor{

    SayOnFail sayOnError
    FeatureInfo featureInfo

      SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {
          this.sayOnError = sayOnError
          this.featureInfo = featureInfo
      }
                                                                     Throw Execption!
      void intercept(IMethodInvocation invocation) {
          try {
             invocation.proceed()
          } catch(Throwable t) {
              def methodName = featureInfo.getFeatureMethod().name
              def voiceName = sayOnError.voice()
              def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"
              try {
                  "say -v $voiceName $sayText".execute()
              } catch (IOException ex) {}
              
              throw t
          }
      }
}
@Document
class LibraryControllerSpec extends Specification{

      @Author('Zan')
      @JiraIssue('MBL-42')
      void "successfully return book to library"() {
          given: "there is a checked out book and a active library"
              Book book = new Book(isdn: '1234567ABC').save(validate: false)
              Library library = new Library(name: 'Carnegie').save()
          and:"the controller has all dependancy"
              CatalogService catalogService = Mock()
              controller.catalogService = catalogService
          and: "submit library id and isdn"
              params.id = library.id
              params.isdn = book.isdn

              when: "submit the form"
                  controller.returnBook()
              and: "get the JSON from the response"
                  JSONElement json = response.getJson()

          then: "verify the returned book is FILED"
              json.isdn == book.isdn
              json.status == 'FILE'
      }
}
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE])
@ExtensionAnnotation(DocumentExtension)

public @interface Document {}
class DocumentExtension extends AbstractAnnotationDrivenExtension<Document> {
    @Override
    void visitSpecAnnotation(Document document, SpecInfo specInfo) {
        specInfo.addListener(new DocumentListener(document))
    }

}
class DocumentListener extends AbstractRunListener{
    private Document document
    File baseDir = new File("src/docs/ref/Tests")
    File output

      DocumentListener(Document document) {
          this.document = document
          baseDir.mkdirs()
      }

    void beforeSpec(SpecInfo specInfo) {...}
    
    void beforeFeature(FeatureInfo featureInfo) {...}

    void beforeIteration(IterationInfo iterationInfo) {...}

    void afterIteration(IterationInfo iterationInfo) {...}

    void afterFeature(FeatureInfo featureInfo) {...}

    void error(ErrorInfo errorInfo) {...}

    void featureSkipped(FeatureInfo featureInfo) {...}

    void afterSpec(SpecInfo specInfo) {...}
    
    void specSkipped(SpecInfo specInfo) {...}

}
Core Spock Links
 http://meetspock.appspot.com/

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

 https://github.com/spockframework/spock


Insightful Posts
 http://kousenit.wordpress.com/2011/08/20/i-think-i-get-spock-mocks-now/

 http://hamletdarcy.blogspot.com/2009/05/peek-inside-spock-framework.html

Extensions
 https://github.com/zanthrash/spock-extensions

 https://github.com/robfletcher/spock-extensions

More Related Content

Recently uploaded

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Recently uploaded (20)

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 

Featured

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

GR8 Conf US: Spock Soup to Nuts

  • 3. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          Book book = new Book(isdn:book & a library" given: "I have a checked out '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          controller.catalogService = new CatalogService()          params.id = library.id for controller" and: "all dependancies          params.isdn = book.isdn and: "we submit the isdn and library id"          controller.returnBook()      JSONElement json = response.getJson()          assertEquals( json.isdn, book.isdn ) when: "submit the form"          assertEquals( json.status, 'FILED' )      } } then: "verify the returned book is FILED" json.isdn == book.isdn json.status == 'FILED'
  • 4. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          Book book = new Book(isdn:book & a library" given: "I have a checked out '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          controller.catalogService = new CatalogService()          params.id = library.id for controller" and: "all dependancies          params.isdn = book.isdn and: "we submit the isdn and library id"          controller.returnBook()      JSONElement json = response.getJson()          assertEquals( json.isdn, book.isdn ) when: "submit the form"          assertEquals( json.status, 'FILED' )      } } then: "verify the returned book is FILED" json.isdn == book.isdn json.status == 'FILED'
  • 5. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          Book book = new Book(isdn:book & a library" given: "I have a checked out '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          controller.catalogService = new CatalogService()          params.id = library.id for controller" and: "all dependancies          params.isdn = book.isdn and: "we submit the isdn and library id"          controller.returnBook()      JSONElement json = response.getJson()          assertEquals( json.isdn, book.isdn ) when: "submit the form"          assertEquals( json.status, 'FILED' )      } } then: "verify the returned book is FILED" json.isdn == book.isdn json.status == 'FILED'
  • 6. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() params.id = library.id and: "we submit the isdn and library id"          params.isdn = book.isdn      controller.returnBook()          JSONElement json = response.getJson() when: "submit the form"               assertEquals( json.isdn, book.isdn ) assertEquals( json.status, 'FILED' ) then: "verify the returned book is FILED" } json.isdn == book.isdn json.status == 'FILED' }
  • 7. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() params.id = library.id and: "we submit the isdn and library id"          params.isdn = book.isdn      controller.returnBook()          JSONElement json = response.getJson() when: "submit the form"               assertEquals( json.isdn, book.isdn ) assertEquals( json.status, 'FILED' ) then: "verify the returned book is FILED" } json.isdn == book.isdn json.status == 'FILED' }
  • 8. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() and: "we submit the isdn and library id"          params.id = library.id      params.isdn = book.isdn          when: "submit the form"          controller.returnBook()      JSONElement json = response.getJson() then: "verify the returned book is FILED" json.isdn == book.isdn assertEquals( json.isdn, book.isdn ) assertEquals( json.status, 'FILED' ) json.status == 'FILED' } }
  • 9. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() and: "we submit the isdn and library id"          params.id = library.id      params.isdn = book.isdn          when: "submit the form"          controller.returnBook()      JSONElement json = response.getJson() then: "verify the returned book is FILED" json.isdn == book.isdn assertEquals( json.isdn, book.isdn ) assertEquals( json.status, 'FILED' ) json.status == 'FILED' } }
  • 10. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() and: "we submit the isdn and library id"          params.id = library.id      params.isdn = book.isdn          when: "submit the form"          controller.returnBook()      JSONElement json = response.getJson() then: "verify the returned book is FILED" json.isdn == book.isdn book.isdn ) assertEquals( json.isdn, json.status == 'FILED' assertEquals( json.status, 'FILED' ) } }
  • 11. @TestFor(LibraryController) @Mock([Book, Library]) class LibraryControllerTests extends Specification{ LibraryControllerSpec {      void testReturnBookToLibrary() { libraray"() { "sucessfully return book to          given: "I have a checked out book & a library"          Book book = new Book(isdn: '1234567ABC').save()          Library library = new Library(name: 'Carnegie').save()          and: "all dependancies for controller"          controller.catalogService = new CatalogService() and: "we submit the isdn and library id"          params.id = library.id      params.isdn = book.isdn          when: "submit the form"          controller.returnBook()      JSONElement json = response.getJson() then: "verify the returned book is FILED" json.isdn == book.isdn book.isdn ) assertEquals( json.isdn, json.status == 'FILED' assertEquals( json.status, 'FILED' ) } }
  • 17. void "add 2 things together"() {     expect:         1 + 2 == 3 'Bat' + 'man' == 'Bat man'         99 + 1 == 100 }
  • 18. Condition not satisfied: 'Bat' + 'man' == 'Bat man'       |       |       Batman  false               1 difference (85% similarity)               Bat(-)man               Bat( )man
  • 19. Condition not satisfied: json.status == 'FILE' |    |      | |    FILED  false |           1 difference (80% similarity) |           FILE(D) |           FILE(-) [id:1, author:null, title:UNKNOWN, status:FILED, class:opi.com.Book, isdn:1234567ABC
  • 20. void "add 2 things together"() {     expect:         1 + 2 == 3 'Bat' + 'man' == 'Bat man'         99 + 1 == 100 }
  • 21. void "add 2 things together"() {     expect:"a + b = c"         a + b == c     where:         a << [1, 'Bat', 99]         b << [1, 'man', 1]         c << [2, 'Batman', '100'] }
  • 22.
  • 23. @Unroll void "add 2 things together"() {     expect:"a + b = c"         a + b == c     where:         a << [1, 'Bat', 99]         b << [1, 'man', 1]         c << [2, 'Batman', '100'] }
  • 24.
  • 25. @Unroll('#a + #b = #c') void "add 2 things together"() {     expect:"a + b = c"         a + b == c     where:         a << [1, 'Bat', 99]         b << [1, 'man', 1]         c << [2, 'Batman', '100'] }
  • 26.
  • 27. @Unroll void "#a + #b = #c"() {     expect:"a + b = c"         a + b == c     where:         a << [1, 'Bat', 99]         b << [1, 'man', 1]         c << [2, 'Batman', '100'] }
  • 28. @Unroll('#a + #b = #c') void "add 2 things together"() {     expect:"a + b = c"         a + b == c     where:         a      | b     || c         1      | 1     || 2         99     | 1     || 100         "Bat" | "man" || "Batman" }
  • 29. void "custom data provider" () {     expect:"a + b = c"         a + b == c     where:         [a, b, c] << new ABCDataProvider() }
  • 30. class ABCDataProvider implements Iterator{     List list = [[1,2,3], [1,99, 100], ['Bat', 'man', 'Batman']]     int position = 0     boolean hasNext() {         position < list.size()     }     Object next() {         def result = list[position]         position += 1         result     }     def size() {         list.size()     }     def close(){ }     void remove() { } }
  • 32. def catalogService = Mock(CatalogService) CatalogService catalogService = Mock()
  • 33. def catalogService = Mock(CatalogService) CatalogService catalogService = Mock()
  • 34. void "test interaction scoping"() {     given:"create mock CatalogService"         CatalogService service = Mock()         controller.catalogService = service     and: "make sure it can file books"         service.isAvailable(_ as Book) >> true     when:"we verify isdn"         params.isdn = book.isdn         def result = controller.verifyISDN()     then:"verify the book was filed"         1 * service.inquired(_)         result == true }
  • 35. void "test interaction scoping"() {     given:"create mock CatalogService"         CatalogService service = Mock()         controller.catalogService = service     and: "make sure it can file books"         service.isAvailable(_ as Book) >> true     when:"we verify isdn" Global         params.isdn = book.isdn         def result = controller.verifyISDN()     then:"verify the book was filed"         1 * service.inquired(_)         result == true }
  • 36. void "test interaction scoping"() {     given:"create mock CatalogService"         CatalogService service = Mock()         controller.catalogService = service     and: "make sure it can file books"         service.isAvailable(_ as Book) >> true     when:"we verify isdn" Local         params.isdn = book.isdn         def result = controller.verifyISDN()     then:"verify the book was filed"         1 * service.inquired(_)         result == true }
  • 37. void "test interaction scoping"() {     given:"create mock CatalogService"         CatalogService service = Mock()         controller.catalogService = service     and: "make sure it can file books"         service.isAvailable(_ as Book) >> true     when:"we verify isdn" Required         params.isdn = book.isdn         def result = controller.verifyISDN()     then:"verify the book was filed"         1 * service.inquired(_)         result == true }
  • 38. void "test interaction scoping"() {     given:"create mock CatalogService"         CatalogService service = Mock()         controller.catalogService = service     and: "make sure it can file books"         service.isAvailable(_ as Book) >> true     when:"we verify isdn" Optional         params.isdn = book.isdn         def result = controller.verifyISDN()     then:"verify the book was filed"         1 * service.inquired(_)         result == true }
  • 39. 1 * catalogService.file(book, library) >> 'FILED' Carnality
  • 40. 1 * catalogService.file(book, library) >> 'FILED' Carnality
  • 41. 1 * catalogService.file(book, library) >> 'FILED' Carnality
  • 42. (0..3) * catalogService.file(book, library) >> 'FILED' Carnality
  • 43. (3.._) * catalogService.file(book, library) >> 'FILED' Carnality
  • 44. (_..3) * catalogService.file(book, library) >> 'FILED' Carnality
  • 46. (_..3) * catalogService.file(book, library) >> 'FILED' Carnality Target Constraint
  • 47. (_..3) * catalogService.file(book, library) >> 'FILED' Carnality Target Constraint
  • 48. (_..3) * _.file(book, library) >> 'FILED' Target Constraint
  • 49. (_..3) * _.file(book, library) >> 'FILED' Target Constraint
  • 50. (_..3) * catalogService.file(book, library) >> 'FILED' Target Constraint Conststraint Method
  • 51. (_..3) * catalogService.file(book, library) >> 'FILED' Target Constraint Conststraint Method
  • 52. (_..3) * catalogService./f.*/(book, library) >> 'FILED' Method Conststraint
  • 53. (_..3) * catalogService./f.*/(book, library) >> 'FILED' Method Conststraint
  • 54. (_..3) * catalogService._(book, library) >> 'FILED' Method Conststraint
  • 55. (_..3) * catalogService._(book, library) >> 'FILED' Method Conststraint
  • 56. (_..3) * catalogService.file(book, library) >> 'FILED' Method Conststraint List Constraint Argument
  • 57. (_..3) * catalogService.file(book, library) >> 'FILED' Method Conststraint List Constraint Argument
  • 58. (_..3) * catalogService.file(_, _) >> 'FILED' Argument List Constraint
  • 59. (_..3) * catalogService.file(*_) >> 'FILED' Argument List Constraint
  • 60. (_..3) * catalogService.file(_ as Book, _) >> 'FILED' Argument List Constraint
  • 61. (_..3) * catalogService.file(_ as Book, !null) >> 'FILED' Argument List Constraint
  • 62. (_..3) * catalogService.file({it.isdn > 1}, !null) >> 'FILED' Argument List Constraint
  • 63. 1 * catalogService.file(book, library) >> 'FILED' Return Values Constraint Argument List
  • 64. 1 * catalogService.file(book, library) >> 'FILED' Return Values Constraint Argument List
  • 65. 3 * catalogService.file(book, library) >>> ['FILED',‘ERROR’] Return Values
  • 66. 3 * catalogService.file(book, library) >> { book.status = FILED return book } Return Values
  • 67. 3 * catalogService.file(book, library) >> { throw new TimeoutException() } Return Values
  • 68. foo.bar() >> { throw new IOException() } >>> [1, 2, 3] >> { throw new RuntimeException() }  Return Values
  • 70. Order Is NOT enforced void "test ordered interactions"() {   ...     then:"verify the book was filed"         1 * service.inquired(_)         2 * service.notifyLibrarian(*_)         result == true }
  • 71. Interactions are NOT ordered void "test ordered interactions"() {   ...     then:"verify an inquiry was add to the book"         1 * service.inquired(_) then:"verify that all librarians are notified "         2 * service.notifyLibrarian(*_)         result == true }
  • 75. @Timeout() @Timeout(10) @Timeout(value=10, unit=TimeUnit.MILLISECONDS)
  • 84. void "test the Use annotation"() {     when:"call awesomeness"         def result = new Dynamic().awesomeness('very')     then:"check result"         result == 'so very awesome' }
  • 85. class DynamicCategory {          static String awesomeness(Dynamic dynamic, String foo) {         “so $foo awesome”     }      }
  • 86. @Use(DynamicCategory) void "test the Use annotation"() {     when:"call awesomeness"         def result = new Dynamic().awesomeness('very')     then:"check result"         result == 'so very awesome' }
  • 87. @Use(DynamicCategory) void "test the Use annotation"() {     when:"call awesomeness"         def result = new Dynamic().awesomeness('very')     then:"check result"         result == 'so very awesome' }
  • 88. void "test the Use annotation"() {     when:"call awesomeness" use(DynamicCategory){         def result = new Dynamic().awesomeness('very') }     then:"check result"         result == 'so very awesome' }
  • 90. ~/.spock/SpockConfig.groovy import spock.util.mop.Use import spock.lang.* import grails.plugin.spock.IntegrationSpec runner {     optimizeRunOrder true     filterStackTrace false     include Unroll, Use     exclude {         baseClass IntegrationSpec     } }
  • 93. import spock.lang.* @SayOnFail class MyFirstSpec extends Specification {   @SayOnFail(value="Danger! Danger!", voice='Zarvox')   def "let's try this!"() {     expect:     Math.max(1, 2) == 3   } }
  • 94. Step 1: Create Annotation @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(SayOnFailExtension) public @interface SayOnFail {     String value() default ''     String voice() default "Alex" }
  • 95. Step 1: Create Annotation @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(SayOnFailExtension) public @interface SayOnFail { Retention     String value() default ''     String voice() default "Alex" }
  • 96. Step 1: Create Annotation @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(SayOnFailExtension) public @interface SayOnFail { Target     String value() default ''     String voice() default "Alex" }
  • 97. Step 1: Create Annotation @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(SayOnFailExtension) public @interface SayOnFail { Extension     String value() default ''     String voice() default "Alex" }
  • 98. Step 1: Create Annotation @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(SayOnFailExtension) Default Values public @interface SayOnFail {     String value() default ''     String voice() default "Alex" }
  • 99. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     }     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 100. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     } Extend     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 101. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     } Override for spec     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 102. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         } Add interceptor to all     } methods     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 103. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     } Override for feature     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 104. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     } Create Interceptor     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 105. Step 2: Create Extension class SayOnFailExtension extends AbstractAnnotationDrivenExtension<SayOnFail>{     @Override     void visitSpecAnnotation(SayOnFail sayOnError, SpecInfo spec) {         spec.features.each {FeatureInfo feature ->             if(!feature.featureMethod.reflection.isAnnotationPresent(SayOnFail)) {                 visitFeatureAnnotation(sayOnError, feature)             }         }     } Add to Feature     @Override     void visitFeatureAnnotation(SayOnFail sayOnError, FeatureInfo feature) {         def interceptor = new SayOnFailInterceptor(sayOnError, feature)         feature.getFeatureMethod().addInterceptor(interceptor)     } }
  • 106. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     }     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 107. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } Implement Interface     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 108. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } Override     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 109. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } run method     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 110. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } if test fails     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 111. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } do your thing     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 112. Step 3: Create Intercepter class SayOnFailInterceptor implements IMethodInterceptor{     SayOnFail sayOnError     FeatureInfo featureInfo     SayOnFailInterceptor(SayOnFail sayOnError, FeatureInfo featureInfo) {         this.sayOnError = sayOnError         this.featureInfo = featureInfo     } Throw Execption!     void intercept(IMethodInvocation invocation) {         try {            invocation.proceed()         } catch(Throwable t) {             def methodName = featureInfo.getFeatureMethod().name             def voiceName = sayOnError.voice()             def sayText = sayOnError.value() ?: "Danger! Failure for: $methodName"             try {                 "say -v $voiceName $sayText".execute()             } catch (IOException ex) {}                          throw t         }     } }
  • 113. @Document class LibraryControllerSpec extends Specification{     @Author('Zan')     @JiraIssue('MBL-42')     void "successfully return book to library"() {         given: "there is a checked out book and a active library"             Book book = new Book(isdn: '1234567ABC').save(validate: false)             Library library = new Library(name: 'Carnegie').save()         and:"the controller has all dependancy"             CatalogService catalogService = Mock()             controller.catalogService = catalogService         and: "submit library id and isdn"             params.id = library.id             params.isdn = book.isdn         when: "submit the form"             controller.returnBook()         and: "get the JSON from the response"             JSONElement json = response.getJson()         then: "verify the returned book is FILED"             json.isdn == book.isdn             json.status == 'FILE'     } }
  • 115. class DocumentExtension extends AbstractAnnotationDrivenExtension<Document> {     @Override     void visitSpecAnnotation(Document document, SpecInfo specInfo) {         specInfo.addListener(new DocumentListener(document))     } }
  • 116. class DocumentListener extends AbstractRunListener{     private Document document     File baseDir = new File("src/docs/ref/Tests")     File output     DocumentListener(Document document) {         this.document = document         baseDir.mkdirs()     }     void beforeSpec(SpecInfo specInfo) {...}          void beforeFeature(FeatureInfo featureInfo) {...}     void beforeIteration(IterationInfo iterationInfo) {...}     void afterIteration(IterationInfo iterationInfo) {...}     void afterFeature(FeatureInfo featureInfo) {...}     void error(ErrorInfo errorInfo) {...}     void featureSkipped(FeatureInfo featureInfo) {...}     void afterSpec(SpecInfo specInfo) {...}          void specSkipped(SpecInfo specInfo) {...} }
  • 117.
  • 118. Core Spock Links http://meetspock.appspot.com/ http://code.google.com/p/spock/wiki https://github.com/spockframework/spock Insightful Posts http://kousenit.wordpress.com/2011/08/20/i-think-i-get-spock-mocks-now/ http://hamletdarcy.blogspot.com/2009/05/peek-inside-spock-framework.html Extensions https://github.com/zanthrash/spock-extensions https://github.com/robfletcher/spock-extensions

Editor's Notes

  1. - Introduce you to spock and spock nomenclature\n- Show you how do data drive your tests\n- Go over testing your object interactions with Spock Mocks\n- Show you some of the built in Extensions\n- And finally show you how to roll your own\n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. - runs before every feature method\n- @Before (JUnit 4)\n- void setup() (JUnit 3)\n\n\n
  61. - runs after every feature method\n- @After (JUnit 4)\n- void tearDown() (JUnit 3)\n
  62. - runs before the first feature method \n- @BeforeClass (JUnit 4)\n
  63. - runs after the last feature method\n- @AfterClass (JUnit 4)\n
  64. - Another structure you can use to test is the &amp;#x2018;expect&amp;#x2019; block\n- &amp;#x2018;When&amp;#x2019; and &amp;#x2018;Then&amp;#x2019; block rolled up into one\n- Best for testing functional behavior that has no side effects\n\n\n
  65. \n
  66. Power asserts become very powerful \n\nwhen you start dealing with complex object.\n\nprovide a wealth of insight as to what is going on with out firing up a debugger.\n
  67. Problems with this:\n\n- Code and data are mixed together and can&amp;#x2019;t be changed independantly\n- once one of the assertions fail the whole test will exit, not running anything after the failure\n\nWhat we want to do is separate our data from our code and have each set of data run in isolation for all other sets\n\nThis is where Spock Paramerterizations come in to play\n\n\n
  68. \n\nUsing the &amp;#x2018;where&amp;#x2019; block and Data Pipes\n\nIntroduce Where Block &amp; Data Pipes\n\nData Pipes connect a data variable to a Data Provider\nData Provider can be anything that has a size() method and implements Iterator.\n - Collection, String, custom.\n\n\n
  69. - On the left we have the test name but no real indication what test failed.\n- Smart assertion shows us Class of the objects that are being compared are the reason for failure\n
  70. Plain @Unroll gives us individual test output, w/ an zero base ordinal position of the test\n
  71. \n
  72. - @Unroll with a value gives us individual test output with as much details of the test and paramaters as we want.\n\n
  73. \n
  74. - The other way to get the details ouput of the parameterized tests is to use the templaing in the method name.\n\n- My preference is to put the test name output in the annotation and kept the method name in tact for human readability\n\nData Pipes are nice but the can get a bit unwieldy if you have a lot of scenerios. This is where data table come in.\n
  75. - Data Tables\n- cleaner, more human readable\n- Table format adds clarity and readibility to your parameterized tests\n- can use double pipe to add a visible break between input params and output params\n- IntellJ will apply formating to make your table look pretty\n\nThere is one more way we can create a Data Provider for our test and that is writing a custom data provider\n
  76. - multiple databinding\n- can be used to get data from db, excel file; or to randomally generate data for test\n
  77. - hasNext()\n- next()\n- size()\n- close() is called but can be a no opt if not needed, useful for db\n
  78. - Like mockito mock are lenient by default. This means that unexpected method calls on mock objects are allowed returning the default value.\n\n- Default return values are based on the methods return type and will be (false, 0 , or null)\n- Mocks are equal only to itself, and has a unique hash code, toString() includes the name of the type it represents.\n
  79. Two way to create a mock\n\nMock() is a method on the Specification superclass\n\ndynamic\n\nstatic\n\n
  80. Two way to create a mock\n\nMock() is a method on the Specification superclass\n\ndynamic\n\nstatic\n\n
  81. Global vs. Local Scoping\n\nOutside of &amp;#x2018;when&amp;#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &amp;#x2018;then&amp;#x2019; block = local = valid only from the preceding &amp;#x2018;when&amp;#x2019; block\n\n
  82. Global vs. Local Scoping\n\nOutside of &amp;#x2018;when&amp;#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &amp;#x2018;then&amp;#x2019; block = local = valid only from the preceding &amp;#x2018;when&amp;#x2019; block\n\n
  83. Global vs. Local Scoping\n\nOutside of &amp;#x2018;when&amp;#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &amp;#x2018;then&amp;#x2019; block = local = valid only from the preceding &amp;#x2018;when&amp;#x2019; block\n\nDon&amp;#x2019;t mix the two up. Local will overwrite global and can cause some confusion in your expected output.\n\nKen Kousin \n\n
  84. Optional vs. Required\n\nOptional = no cardinality must have an explicit return value\n - act as more of a Stub where we are interested in what controlling what is coming out of this method\n - not woried about the verifying that the method is called\n\nRequired = has cardinality and may have a return value\n- more of a true mock where we are interested in the verifying the behavior of the system\n
  85. Optional vs. Required\n\nOptional = no cardinality must have an explicit return value\n - act as more of a Stub where we are interested in what controlling what is coming out of this method\n - not woried about the verifying that the method is called\n\nRequired = has cardinality and may have a return value\n- more of a true mock where we are interested in the verifying the behavior of the system\n
  86. Ananomy of a interaction\n
  87. Ananomy of a interaction\n
  88. Cardnality\n\nexactly &amp;#x2018;n&amp;#x2019; number of times\n\n
  89. Cardnality\n\nexplicit range\n\n
  90. Cardnality\n\nat least 3 times with an wildcarded upper bound\n\n
  91. Cardnality\n\nat most 3 times with an wildcarded lower bound\n\n
  92. InteractionNotSatisfiedError\n
  93. Target Constraint\n\nCan be for a specific mock instance\n\n
  94. Target Constraint\n\nCan be for a specific mock instance\n\n
  95. Target Constraint\n\nCan be for a specific mock instance\n\n
  96. Target Constraint\n\nCan be for a specific mock instance\n\n
  97. Target Constraint\n\nor a wild carded (all mocks)\n
  98. Target Constraint\n\nor a wild carded (all mocks)\n
  99. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  100. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  101. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  102. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  103. Method Constraints\n\nRegEx.\n\n
  104. Method Constraints\n\nRegEx.\n\n
  105. Method Constraints\n\nCan be wild carded for a call to that method signature on any mock object\n\n
  106. Method Constraints\n\nCan be wild carded for a call to that method signature on any mock object\n\n
  107. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  108. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  109. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  110. Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
  111. Method Constraints\n\nwildcards: Any 2 arguments\n\n
  112. Method Constraints\n\n*_ matches any argument list\n\n
  113. Method Constraints\n\nAny param that is an instance of a Class\n\n\n\n
  114. Method Constraints\n\nSecond param is any non-null value\n\n\n\n
  115. Method Constraints\n\ncustom param as a closure\n\n\n
  116. Return Values\n\nStubs: that provide a return value for the mock call\n\nNeed to have a return value if you make the interaction optional by not have a cardnality.\n\n
  117. Return Values\n\nStubs: that provide a return value for the mock call\n\nNeed to have a return value if you make the interaction optional by not have a cardnality.\n\n
  118. Return Values\n\nStubs: that provide a return value for the mock call\n\nNeed to have a return value if you make the interaction optional by not have a cardnality.\n\n
  119. Return Values\n\nStubs: that provide a return value for the mock call\n\nNeed to have a return value if you make the interaction optional by not have a cardnality.\n\n
  120. Return Values\n\nReturn values can be in be itererated over for each call to it.\n\nLast in list will be called indefinitely \n\n\n\n
  121. Return Values: Custom\n\n\n\n\n\n
  122. Return Values: Custom Action\n\nStub out the throwing of an error\n\n\n\n\n\n
  123. Return Values: Chained\n\nInteractions are NOT ordered\n\nIf declared in the same &amp;#x2018;then&amp;#x2019; block the interactions enforcement of order dose not exist\n\nCan override this with multiple THEN blocks and putting interactions within\n\n\n\n\n\n
  124. Return Values: Chained\n\nInteractions are NOT ordered\n\nIf declared in the same &amp;#x2018;then&amp;#x2019; block the interactions enforcement of order dose not exist\n\nCan override this with multiple THEN blocks and putting interactions within\n\n\n\n\n\n
  125. If declared in the same &amp;#x2018;then&amp;#x2019; block the interactions enforcement of order dose not exist\n\nCan override this with multiple THEN blocks and putting interactions within\n
  126. If declared in the same &amp;#x2018;then&amp;#x2019; block the interactions enforcement of order dose not exist\n\nCan override this with multiple THEN blocks and putting interactions within\n\nWrong Invocation Order Exception\n
  127. Extensions are m\n
  128. Type and Method annotation\n\nUnrolls the paramertized test and replaces the test name as\n
  129. Field Level only annotation\n\nPrimarily used for marking variables outside of a feature (ie. setup()) that will be used between iterations.\n\ncan achieve the same thing by marking it static.\n\nwill be shared by all test methods\n
  130. Type and Method level annotation\n\nInvoked on the regular test framework thread\nThe the timer runs out an SpockTimeoutError will be thrown\n\nValue is an integer\nDefault unit is TimeUnit.SECONDS\njava.util.concurrent.TimeUnit\n\n
  131. Type Only annotation\n\nIndicates that all feature methods should be run sequentially from the top down\n\nif one method fails the remaining methods will be skipped\n\n
  132. Type &amp; Method annotation\n\nIndicates the method should not be run\n\n
  133. Method Only annotation\n\nIndicates that all other feature methods should be skipped.\n\nCan set this on multiple methods\nHandy for running a single test in isolation\n
  134. Method &amp; Class annotation\n\nTakes a closure that should return something that resolves to a truthy Groovy statement.\n\n\n\n\n
  135. Method &amp; Class annotation\n\nTakes a Throwable class that the SUT is expected to throw\n\n\n
  136. Field annotation\n\nBy default calles the &amp;#x2018;close&amp;#x2019; method on the object that it is annotated during the cleanup phase of the test.\n\nEquivilate of calling close in cleanup\n\nquite = true will not report any exceptions that are thrown during the execution of the method.\n\n\n\n\n
  137. Type &amp; Method annotation\n\nActivate one or more groovy Categories while the class or feature method executes\n\n\n\n
  138. A Do-Nothing class that get methods dynamically added to it a runtime\n\nThis is done all the time in Grails (GORM). Plugins do it.\n\nSooner or later your going to run into it.\n\nMostly solved with intergration tests :(\n\n\n\n\n
  139. \n\n\n\n
  140. \n\n\n\n
  141. \n\n\n\n
  142. If you want to learn more about Categories Ken Kousen is giving a talk about metaprograming for fun an profit right after this talk\n\n\n\n
  143. Type and Method\n\nConfines the changes made to the metaclasses of the specified class to the annotated scope\n\nLifecycle:\nfor a method\n- on setup the existing classes meta classes is removed and an a new temporay one is added and registered.\n- on cleanup the original metaclass is restored\nfor a spec/classe\n- on setupSpec\n- on cleanupSpec\n\nHelpful for preventing test pollution where the outcomes of test are different when they are run together vs. individually.\n\n\n\n
  144. \n
  145. \n
  146. Using the Spock API to roll your own extensions\n\n2 types\n- Method interceptors via the extending org.spockframework.runtime.extension.AbstractMethodInterceptor.java\n- intercept method invocation\n- invocation.proceed\n- can change test outcome or behavior\n- can throw exceptions\n- Runtime Listener via org.spockframework.runtime.AbstractRunListener\n- Listen passively\n- Can Inspect the tests and test results\n- Can be used to programatically skipp a spec or feature\n- Should not throw errors\n\n
  147. \n
  148. \n
  149. \n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. \n
  157. \n
  158. \n
  159. \n
  160. \n
  161. \n
  162. \n
  163. \n
  164. \n
  165. \n
  166. \n
  167. Using a runtime listener\n
  168. \n
  169. \n
  170. \n
  171. \n
  172. \n