Presentation is loading. Please wait.

Presentation is loading. Please wait.

{ Testing++ With Spock & Ian Kelly.

Similar presentations


Presentation on theme: "{ Testing++ With Spock & Ian Kelly."— Presentation transcript:

1 { Testing++ With Spock & Geb @kellizergithub.com/kellizer Ian Kelly

2 {

3 Spock The Logical Testing Framework Spock – A logical way to test Groovy based framework Follows Behaviour Driven Design (BDD) Paradigms (Tests are live documents) Works with IDEs/CI servers Out of the Box & live with existing tests We are working with: Spock 0.7 Geb 0.9 Groovy 2.0 (And Bring Fun to Testing)

4 Anatomy of a Spock Test Specification System Under Specification Feature Fixture Collaborator

5 Our First Specification All Specifications extend spock.lang.Specification Test Execution class DogSpecification extends Specification { def "Dogs should bark when told to speak"() { setup: def chip = new Dog() expect: chip.speak() == 'WOOF' } /** * System Under Specification */ public class Dog { public String speak() { return "WOOF"; }

6 Feature Specifications Hold 1 or more Feature(s) Feature Method (The Test) class DogSpecification extends Specification { def "Dogs should bark when told to speak"() { setup: def chip = new Dog() expect: chip.speak() == 'WOOF' }

7 Lets Run The Test Dogs Should Say bark when told to speak Test Passed

8 Lets Run The Test What happens if chip speaks like a duck? Test Failed

9 Feature Methods Feature Method Contain Blocks setup: or given: - to setup the test expect: - to test when there are no side effects when:/then: – apply the behaviour then validate the state cleanup: – called at the end of the feature execution where: – provides datasets to a feature Spock Recognise Feature Methods and executes them class DogSpecification extends Specification { def "Dogs should bark when told to speak"() { setup: def chip = new Dog() expect: chip.speak() == 'WOOF' }

10 Feature Methods - Blocks setup: (given:) Initialization of the feature Sits at the top of the method Can be termed as ‘given:’ Optional & non-repeatable

11 Feature Methods - Blocks when/then: when: stimulates the SUS then: validates the SUS Expressions are assertions def "Check that Customer can have a zero balance"() { setup: def bankAccountWithOutOverdraft = new BankAccount() when: "test a number of deposits and withdrawals" bankAccountWithOutOverdraft.depositFunds(100) bankAccountWithOutOverdraft.withdrawAmount(100) then: "The account should be zero and no exception thrown" bankAccount bankAccount.isActiveAccount() bankAccount.balance == 0 }

12 Feature Methods - Blocks expect: Combines a stimulus and response More limited than a then: block Use with pure functions Stimulus def "2 should be larger than 1"() { expect: Math.max(1, 2) == 2 } Expected Response

13 Feature Methods - Blocks cleanup: Use to free resources called Even if expectation is thrown Comparable to the java finally block def "Test with a tmp file"() { setup: def file = new File("/new/file/tmp.out") //Do stuff with the FS file cleanup: file.delete() }

14 Feature Methods - Blocks then: with old: Captures the account balance before applying stimulus def bankAccount = new BankAccount() def "Check that the bank account accepts a deposit"() { when: "we deposit 100zl into the the account" bankAccount.depositFunds(100) then: "expect the old bal. to be 0 and new bal. to be 100" old(bankAccount.balance) == 0 bankAccount.balance == 100 }

15 Feature Methods - Blocks Descriptions on blocks: def bankAccount = new BankAccount() def "Check that the bank account accepts a deposit"() { when: "we deposit 100zl into the the account" bankAccount.depositFunds(100) then: "expect the old bal. to be 0 and new bal. to be 100" old(bankAccount.balance) == 0 bankAccount.balance == 100 }

16 Feature Methods - Blocks then/and def "Check that Authorised Overdrafts work as expected"() { setup: def bankAccountWithOverdraft = new BankAccount() bankAccountWithOverdraft.setAllowedOverDraft(500) when: "test a number of deposits and withdrawals" bankAccountWithOverdraft.depositFunds(100) and: "Note the and label" bankAccountWithOverdraft.withdrawAmount(50) and: "Balance should be 50 at this point" bankAccountWithOverdraft.withdrawAmount(30) and: "Balance should be 20 at this point" bankAccountWithOverdraft.withdrawAmount(100) then: "We should now be left with a balance of 80 overdrawn" bankAccountWithOverdraft.isInOverdraft() bankAccountWithOverdraft.balance == -80 }

17 Feature Methods Thrown() within the then: block Expected outcome to be a thrown InsufficientFundsException def "Check that Customer cannot go over their allowed amount"() { setup: def bankAccountWithOutOverdraft = new BankAccount() when: "test a number of deposits and withdrawals" bankAccountWithOutOverdraft.depositFunds(100) bankAccountWithOutOverdraft.withdrawAmount(110) then: thrown(InsufficientFundsException) }

18 Feature Methods notThrown() Expected outcome to be a thrown InsufficientFundsException

19 Driving Tests with Data

20 Feature Methods - Blocks where: Same Logic, Different Data Allows for Data Driven Feature Methods Last in Method – Cannot be repeated Data can be fed from external sources Support multiple formats | && || pipes to delimit values (Data Tables)

21 Feature Methods - Blocks Where def "Validate Age Calculation From DOB"() { when: Date date = new SimpleDateFormat("dd-mm-yyyy", Locale.ENGLISH).parse(dob) def calculatedAge = dateService.ageFromDOB(date) then: calculatedAge == age where: Dob <<["01-02-1980”,"01-01-1990","12-12-1958"] age << [33, 23, 55] }

22 def "Validate Age Calculation From DOB"() { when: Date date = new SimpleDateFormat("dd-mm-yyyy", Locale.ENGLISH).parse(dob) def calculatedAge = dateService.ageFromDOB(date) then: calculatedAge == age where: dob || age "01-02-1980" || 33 "01-01-1990" || 23 "12-12-1958" || 55 } Feature Methods - Blocks Where – Data Tables

23 Feature Methods - Blocks Where – Database Driven @Unroll //see all the tests. def "maximum of two numbers, a=#a, b=#b so max would be #c"() { expect: Math.max(a, b) == c where: [a, b, c] << sql.rows("select a, b, c from maxdata") }

24 Feature Methods - Blocks where: Wrong Stimulus Test Output

25 @Unroll Reports Feature Executions independently Has no affect on the execution, just reporting

26 Mocking

27 Feature Methods - Mocking Method Argument Object being mocked Return Object Cardinality

28 Feature Methods - Mocking CardinalityDescription NoneOptional (n.._)At least n times n*Exactly n times (_..n)Up to n times

29 Feature Methods - Mocking Argument ConstraintDescription valueArgument Equals value !valueArgument Not Equal *_Any number of arguments _Any argument !nullNon-null Argument

30 Feature Methods - Mocking Return ValuesDescription >>single return value, repeated indefinitely >>>multiple return values, last one repeated indefinitely >> { > }custom action

31 Feature Methods - Mocking Order of interaction not defined Order of interaction not defined Extra then: guarantees ordering Extra then: guarantees ordering def "example showing the ordered interaction"() { given: def subscriber1 = Mock(Subscriber) Subscriber subscriber2 = Mock() Subscriber subscriber3 = Mock() Publisher publisher = new StandardPublisher(subscribers: [subscriber1, subscriber2,subscriber3]); Event event = Mock() when: publisher.send(event) then: 1 * subscriber1.receive(event) 1 * subscriber2.receive(event) // order of interaction within same then-block is not defined; // hence, subscriber1 might be notified either before or after subscriber2 then: // must come after all interactions in previous then-blocks; // hence, subscriber3 must be notified after both subscriber1 and subscriber2 1 * subscriber3.receive(event) }

32 def "load customer and apply airmiles but customer doesn't exist"() { when: airMilesProcessor.applyAirMiles(332211, 4000) then: 1 * customerRepository.findById(332211) >> null thrown(IllegalStateException) } Feature Methods - Mocking Expected 2 invocations, (only 1 registered) Output

33 More Goodness

34 Fields Created per feature method or shared New Instance Per Feature Method @Shared Annotation Sharing Fields between Feature Methods class DatabaseDrivenSpecification extends Specification { @Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver") // normally an external database would be used, // and the test data wouldn't have to be inserted here def setupSpec() { sql.execute("create table maxdata (id int primary key, a int, b int, c int)") sql.execute("insert into maxdata values (1, 3, 7, 7), (2, 5, 4, 5), (3, 9, 9, 9)") }

35 Fixture Methods Invoke setupSpec() Invoke setup() Invoke feature method() Invoke cleaup() Invoke cleanupSpec()

36 Extensions @ IgnoreIf Ignores if a certain condition is true @ Requires Inverse of @IgnoreIf – only runs when condition is true

37 Extensions @ IgnoreRest Ignores all other methods in the specification @ Ignore Will ignore test(s) Can be place at feature or specification level @ IgnoreIf Ignores if a certain condition is true

38 @Timeout Allows you to timeout your test if not completed in a predetermined time Result @Timeout(value = 5) def "timeout feature method if not completed after 5 seconds"() { def person = new Customer(name: "Fred", age: 22) when: person.age = 42 Thread.sleep(7000) then: person.age == 42 }

39 @AutoCleanup Will invoke close() on field (can be any method) Exception will be reported but not classed as a failure Preferred to cleanup()/cleanupSpec( @AutoCleanup(quiet = true) def input = new FileInputStream("myfile.txt") def "some input stream tests"() { // test uses file input stream }

40 Feature Methods Extended assert Test Output def "Check that Customer can have a balance of total plus 1"() { setup: def bankAccountWithOutOverdraft = new BankAccount() when: bankAccountWithOutOverdraft.depositFunds(100) then: assert bankAccountWithOutOverdraft.balance == 100, “bank account total should be 100" }

41 More Gems - @Stepwise Run in Defined Order If 1 Test fails, the remainder get skipped @Stepwise class StepwiseSpecification extends Specification { def "step 1"() { println("step 1") expect: true } def "step 2"() { expect: true println("step 2") } def "step 3"() { expect: true println("step 3") }

42 More Gems - @Stepwise @Stepwise class StepwiseSpecification extends Specification { def "step 1"() { println("step 1") expect: false } def "step 2"() { expect: true println("step 2") } def "step 3"() { expect: true println("step 3") }

43 class IncludeExcludeExtensionSpec extends Specification { static { //System.setProperty "spock.configuration", "IncludeFastConfig.groovy" // Alternatively, try this: System.setProperty "spock.configuration", "ExcludeSlowConfig.groovy" } @Fast def "a fast method"() { expect: true } @Slow def "a slow method"() { expect: true } def "a neither fast nor slow method"() { expect: true } } More Gems - @ More Gems - @ Include/Exclude ExcludeSlowConfig.groovy runner { exclude Slow }

44 Geb

45 Geb Awesome Geb Awesome Browser Automation Introduces the power of WebDriver to your tests Complements Spock but is Independent Project Uses Expressiveness of Groovy Has a jQuery like API (is not jQuery) Has Domain Modelling Support via Pages/Modules

46 Geb – Supported Browsers First Class Support Firefox Internet Explorer Google Chrome Mobile (Ipad/Iphone/Android) Remote Browsers Headless Browser PhantomJS W3C Standard http://www.w3.org/TR/webdriver/ WebDriver

47 Geb abstracts WebDriver Interactions

48 Spock Our Test Specification Geb/Spock Adapter Geb Support for Spock Geb DSL The Geb DSL Webdriver Browser API support Browser FF Chrome IE etc. Web Application What we are testing Spock & Geb

49 class LoginStorySpecification extends GebReportingSpecWithPause { def "Login with an correct password"() { given: go "/login.html" $("form").with { username = 'admin' password = 'password' remember = true } when: $("button").click() then: title == "Authenticated User" } Spock Test (Using Geb)

50 Geb – Page Object Pattern Model web pages for re-useable and maintainable code Reduces duplication Abstract the HTML implementation structure Changes happen Simply change in a single location def "Login to The Secure Admin Server"() { when: to LoginPage login("admin", "password") then: at AuthenticatedAdminPage }

51 Geb – Page Object Pattern Simply change elements in a single location class AuthenticatedAdminPage extends Page { static content = { //optional content administratorsName(required: false) { $("h2") } //stacked users(wait: true) { $("li.span5.clearfix") } user { i -> users[i] } userName { i -> users[i].find("h4", 0) } } }

52 Geb – jQuery(ish) API Copying jQuery has many benefits CSS Content Lookup Selecting content on a page Traversing to/around content Methods for retrieving relative content Fluent API Geb (like jQuery) uses a “ $ ” function Geb != jQuery

53 Geb – jQuery(ish) API Selecting content – DOM Searching //match all ‘div’ elements on the page $("div”) //match all ‘div’ with a title attribute value of ‘section’ $("div", title: "section") //match the first ‘div’ with the class ‘main’ $("div.main", 0) //match all 'div' elements with the class ‘main’ $("div.main”) Navigator Object

54 Geb – jQuery(ish) API Selecting content – CSS Selector $("div.some-class p:first[title='something']")

55 Geb – jQuery(ish) API Selecting content – Index & Ranges a b c $("p", 0).text() == "a” $("p", 2).text() == "c” $("p", 0..1)*.text() = ["a", "b"] $("p", 1..2)*.text() = ["b", "c"]

56 Geb – jQuery(ish) API Index & Ranges p1 p2 $("p", attr1: "a", attr2: "b").size() == 1 $("p", attr1: "a").size() == 2 $("p", text: "p1", attr1: "a").size() == 1 $("p", text: ~/p./).size() == 2 $("p", text: startsWith("p")).size() == 2 $("p", text: endsWith("2")).size() == 1 $("p", text: ~/p./).size() == 2

57 Geb – jQuery(ish) API Pattern Matching KeywordDescription startsWith Matches values that start with the given value contains Matches values that contain the given value anywhere endsWith Matches values that end with the given value containsWord Matches values that contain the given value surrounded by either whitespace or the beginning or end of the value notStartsWith Matches values that DO NOT start with the given value notContains Matches values that DO NOT contain the given value anywhere notEndsWith Matches values that DO NOT end with the given value notContainsWord Matches values that DO NOT contain the given value surrounded by either whitespace or the beginning or end of the value

58 Geb – jQuery(ish) API Traversing content - Finding & Filtering geb $("div").find(".b") //p.b $("div").filter(".b") //div.b $(".b").not("p") //div.b $("div").has("p") //div containing p //div containing the input with a type attribute of “text” $("div").has("input", type: "text")

59 Geb – jQuery(ish) API Traversing content $("p.d").previous() // 'p.c' $("p.e").prevAll() // 'p.c' & 'p.d' $("p.d").next() // 'p.e' $("p.c").nextAll() // 'p.d' & 'p.e' $("p.d").parent() // 'div.b' $("p.c").siblings() // 'p.d' & 'p.e' $("div.a").children() // 'div.b' & 'div.f'

60 Geb – Screenshots/HTML Reports Automatically captures the State As a screenshot of the page Also captures the HTML content Optional (extend GebReportingSpec )

61 Geb – Configuration & Driver Management Looks for GebConfig class or GebConfig.groovy file on classpath Manages and optimises webdriver instances & clearing cookie state

62 Geb – Direct Downloading Lots of work behind the scenes Browser.drive { go "http://myapp.com/login" // login username = "me" password = "secret" login().click() // now find the pdf download link def downloadLink = $("a.pdf-download-link") // now get the pdf bytes def bytes = downloadBytes(downloadLink.@href)

63 Geb – CI Test Reports

64 Bring Gradle to party

65  Spock – http://docs.spockframework.org http://docs.spockframework.orghttp://docs.spockframework.org  Geb – http://www.gebish.org http://www.gebish.org  Sauce Labs – http://www.saucelabs.com www.saucelabs.com  Gradle – http://www.gradle.org/ Thank You @kellizergithub.com/kellizer Ian Kelly


Download ppt "{ Testing++ With Spock & Ian Kelly."

Similar presentations


Ads by Google