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' )
}
}
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() { }
}
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
}
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
}
84. void "test the Use annotation"() {
when:"call awesomeness"
def result = new Dynamic().awesomeness('very')
then:"check result"
result == 'so very 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'
}
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'
}
}
- 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
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
- runs before every feature method\n- @Before (JUnit 4)\n- void setup() (JUnit 3)\n\n\n
- runs after every feature method\n- @After (JUnit 4)\n- void tearDown() (JUnit 3)\n
- runs before the first feature method \n- @BeforeClass (JUnit 4)\n
- runs after the last feature method\n- @AfterClass (JUnit 4)\n
- Another structure you can use to test is the &#x2018;expect&#x2019; block\n- &#x2018;When&#x2019; and &#x2018;Then&#x2019; block rolled up into one\n- Best for testing functional behavior that has no side effects\n\n\n
\n
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
Problems with this:\n\n- Code and data are mixed together and can&#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
\n\nUsing the &#x2018;where&#x2019; block and Data Pipes\n\nIntroduce Where Block & 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
- 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
Plain @Unroll gives us individual test output, w/ an zero base ordinal position of the test\n
\n
- @Unroll with a value gives us individual test output with as much details of the test and paramaters as we want.\n\n
\n
- 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
- 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
- multiple databinding\n- can be used to get data from db, excel file; or to randomally generate data for test\n
- hasNext()\n- next()\n- size()\n- close() is called but can be a no opt if not needed, useful for db\n
- 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
Two way to create a mock\n\nMock() is a method on the Specification superclass\n\ndynamic\n\nstatic\n\n
Two way to create a mock\n\nMock() is a method on the Specification superclass\n\ndynamic\n\nstatic\n\n
Global vs. Local Scoping\n\nOutside of &#x2018;when&#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &#x2018;then&#x2019; block = local = valid only from the preceding &#x2018;when&#x2019; block\n\n
Global vs. Local Scoping\n\nOutside of &#x2018;when&#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &#x2018;then&#x2019; block = local = valid only from the preceding &#x2018;when&#x2019; block\n\n
Global vs. Local Scoping\n\nOutside of &#x2018;when&#x2019; block = global = valid from point of definition to end of feature method\n\nInside of &#x2018;then&#x2019; block = local = valid only from the preceding &#x2018;when&#x2019; block\n\nDon&#x2019;t mix the two up. Local will overwrite global and can cause some confusion in your expected output.\n\nKen Kousin \n\n
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
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
Ananomy of a interaction\n
Ananomy of a interaction\n
Cardnality\n\nexactly &#x2018;n&#x2019; number of times\n\n
Cardnality\n\nexplicit range\n\n
Cardnality\n\nat least 3 times with an wildcarded upper bound\n\n
Cardnality\n\nat most 3 times with an wildcarded lower bound\n\n
InteractionNotSatisfiedError\n
Target Constraint\n\nCan be for a specific mock instance\n\n
Target Constraint\n\nCan be for a specific mock instance\n\n
Target Constraint\n\nCan be for a specific mock instance\n\n
Target Constraint\n\nCan be for a specific mock instance\n\n
Target Constraint\n\nor a wild carded (all mocks)\n
Target Constraint\n\nor a wild carded (all mocks)\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nRegEx.\n\n
Method Constraints\n\nRegEx.\n\n
Method Constraints\n\nCan be wild carded for a call to that method signature on any mock object\n\n
Method Constraints\n\nCan be wild carded for a call to that method signature on any mock object\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nExplicit: Define the method interaction to be called\n\n
Method Constraints\n\nwildcards: Any 2 arguments\n\n
Method Constraints\n\n*_ matches any argument list\n\n
Method Constraints\n\nAny param that is an instance of a Class\n\n\n\n
Method Constraints\n\nSecond param is any non-null value\n\n\n\n
Method Constraints\n\ncustom param as a closure\n\n\n
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
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
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
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
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
Return Values: Custom\n\n\n\n\n\n
Return Values: Custom Action\n\nStub out the throwing of an error\n\n\n\n\n\n
Return Values: Chained\n\nInteractions are NOT ordered\n\nIf declared in the same &#x2018;then&#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
Return Values: Chained\n\nInteractions are NOT ordered\n\nIf declared in the same &#x2018;then&#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
If declared in the same &#x2018;then&#x2019; block the interactions enforcement of order dose not exist\n\nCan override this with multiple THEN blocks and putting interactions within\n
If declared in the same &#x2018;then&#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
Extensions are m\n
Type and Method annotation\n\nUnrolls the paramertized test and replaces the test name as\n
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
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
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
Type & Method annotation\n\nIndicates the method should not be run\n\n
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
Method & Class annotation\n\nTakes a closure that should return something that resolves to a truthy Groovy statement.\n\n\n\n\n
Method & Class annotation\n\nTakes a Throwable class that the SUT is expected to throw\n\n\n
Field annotation\n\nBy default calles the &#x2018;close&#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
Type & Method annotation\n\nActivate one or more groovy Categories while the class or feature method executes\n\n\n\n
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
\n\n\n\n
\n\n\n\n
\n\n\n\n
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
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
\n
\n
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