bdd state-of-the-union (devoxx and xpdays version)

90
John Ferguson Smart Behavior-Driven Development on the JVM A State of the Union

Upload: wakaleo-consulting

Post on 19-Jun-2015

1.874 views

Category:

Documents


2 download

TRANSCRIPT

  • 1. Behavior-Driven Development on the JVMA State of the UnionJohn Ferguson Smart

2. ConsultantTrainer Mentor AuthorSpeakerCoderJohn Fer guson S mar t 3. What is BDD? 4. Business Analyst Common LanguageBusinessDeveloperTester 5. ExecutableSpecifications 6. Outsid e In 7. Value Driven 8. So why use BDD?Only build features that add real valueLess wasted eortBetter communicationHigher quality, better tested productTraceability 9. BDD - Requirements Analysis andCommunication 10. Goals Capabilities FeaturesStoriesExamples/ScenariosAcceptance Criteria 11. 11 Successful projects start with a shared visionWe are going to build an online classieds website 12. 12You dene goals to achieve your visionWe can increaseadvertising revenue byletting sellers post theirclassied ads online Lets get more sales for our advertisers by making the ads easier to nd online. 13. Determining the value of a goal A good goal should add value to the businessIncrease revenueReduce costsAvoid future costsProtect revenueIncrease advertising revenue by allowing sellers to post classied ads onlineReduce the costs involved in publishing a classied ad by allowing sellers to post them online themselves. Prevent current customers switching to a competing product by providing support for online credit card payments 14. What does the customer really need? I want users to be able to search for products by keywordWhy?So that potential buyers can nd the articles they want Why? So that our sellers can sell their stuff fasterWhy?So that they keep selling their stuff on our site Why?So that we keep earning money when they post their ads with us 15. What does the customer really need?Good teams push back! Users tend to express requirements as implementations We need to nd the business need behind the suggested implementation I want users to be able to search by keywordSo in order to make the site more attractive for sellersBuyers need to be able to nd things easilyA search feature might be one way to achieve this But full-text searches might be more eective than keywords 16. Features and capabilities help deliver these goals Lets get more sales for our advertisers by making the ads easier to nd online.Notify potential buyers about new itemsIn Search for online of advertised articles order to increase sales adsAs aorder to increase sales of advertised articles In sellerI want previous buyers to know about new itemsAs a sellerthat theybuyers be interested in buying ads forI want might to be able to easily find articles they want to buy 17. Feature Injection - what features do you do first?Our goals say what business value we need to deliverWe implement the minimum features required todeliver this business valueSearch for online adsThe goal comes first In order to increase sales of advertised articlesThe stakeholder is As a seller secondary I want buyers to be able to easily find ads for articles they want to buyThe feature must be required to achieve the goal 18. 18We use examples and stories to explore the featuresSearch for online adsSearching by category Searching by keyword and category 19. 19We use examples and stories to explore the featuresSearch for online ads Searching by keyword and location Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of ads for puppies for sale. 20. 20Examples and scenarios become acceptance criteriaSearching by keyword and location Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of ads for puppies for sale. Scenario: Searching by keyword and location Given Sally wants to buy a present for her son When she looks for the present in a given category Then she should obtain a list of matching ads for sale. Examples: Present Category Expected Keywordspuppy Pets & Animals labrador kitten Pets & Animals burmese kittenToysuffy cat Acceptance Criteria illustrate and validate the stories 21. Organize your requirements 22. Requirements can come from many sources... 23. Requirements can come from many sources... 24. Goal:In order to increase revenue from commissions on classified ads salesAs the head of the classified ads departmentI want to increase the number of items sold via our classified ads Capability In order to increase the number of items I sell Feature As a seller In order to increase sales of advertised articles I want buyers to be able to view ads for itemsAs a seller they might want to purchase I want potential buyers to be able to display only the ads for articles that they might be interested in purchasing. Story In order to find the items I am interested in faster Keep them organized!As a buyer I want to be able to list all the ads with a particular keyword in the description or title. 25. 25BDD - Test Automation and Beyond 26. 26The original Java BDD framework 27. 27search_by_keyword_and_location.story Narrative: In order to increase sales of advertised articles As a seller I want buyers to be able to easily find ads for articles they want to buy Scenario: Searching by keyword and location Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of ads for puppies for sale. 28. 28search_by_keyword_and_location.story Scenario: Searching by keyword and location Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of ads for puppies for sale. Scenario: Searching by keyword and location Given Sally wants to buy a for her son When she looks for in the category Then she should obtain a list of ads for for sale. Examples: |present|category |expected| |puppy|Pets & Animals | puppies| |kitten |Pets & Animals | kittens| |seiko|Jewellery & Watches| watch | 29. 29search_by_keyword_and_location.story Scenario: Searching by keyword and location 1 Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of ads for puppies for sale. 30. 30search_by_keyword_and_location.story Scenario: Searching by keyword and location 1 Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of puppy ads public class SearchAdsSteps { @Steps2 BuyerSteps buyer; @Given("Sally wants to buy a $present for her son") public void buyingAPresent(String present) { buyer.opens_home_page(); } @When("she looks for $keyword in the $category category") public void adSearchByCategoryAndKeyword(String category, String keyword) { buyer.chooses_category_and_keywords(category, keyword); buyer.performs_search(); } @Then("she should obtain a list of $keyword ads") public void shouldOnlySeeAdsContainingKeyword(String keyword) { buyer.should_only_see_results_with_titles_containing(keyword); } } 31. 31search_by_keyword_and_location.story Scenario: Searching by keyword and location 1 Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of puppy ads public class SearchAdsSteps { @Steps2 BuyerSteps buyer; public class BuyerStories extends JUnitStories { @Given("Sally wants to buy a $present for her son") public BuyerStories() { public void buyingAPresent(String present) { 3 configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(false) buyer.opens_home_page(); .doIgnoreFailureInView(true).doVerboseFailures(true).useThreads(2).useStoryTimeoutInSecs(60); } } @Override @When("she looks for $keyword { the $category category") public Configuration configuration() in public void adSearchByCategoryAndKeyword(String category, String keyword) { return new MostUsefulConfiguration(); } buyer.chooses_category_and_keywords(category, keyword); buyer.performs_search(); @Override } public InjectableStepsFactory stepsFactory() { return new InstanceStepsFactory(configuration(), new TraderSteps(new TradingService()), new AndSteps()); } @Then("she should obtain a list of $keyword ads") public void shouldOnlySeeAdsContainingKeyword(String keyword) { @Override buyer.should_only_see_results_with_titles_containing(keyword); protected List storyPaths() { String codeLocation = codeLocationFromClass(this.getClass()).getFile(); } return new StoryFinder().findPaths(codeLocation, asList("**/*.story", } "**/traders_can_be_subset.story"), asList(""), "file:" + codeLocation); } } 32. 32search_by_keyword_and_location.story Scenario: Searching by keyword and location 1 Given Sally wants to buy a puppy for her son When she looks for puppy in the Pets and Animals category Then she should obtain a list of puppy ads public class SearchAdsSteps { @Steps2 BuyerSteps buyer; @Given("Sally wants to buy a $present for her son") public void buyingAPresent(String present) { buyer.opens_home_page(); } @When("she looks for $keyword in the $category category") public void adSearchByCategoryAndKeyword(String category, String keyword) { buyer.chooses_category_and_keywords(category, keyword); buyer.performs_search(); } @Then("she should obtain a list of $keyword ads") public void shouldOnlySeeAdsContainingKeyword(String keyword) { buyer.should_only_see_results_with_titles_containing(keyword); } 3 }public class BuyerStories extends ThucydidesJUnitStories {} 33. 33Now available in JVM flavor! 34. 34Feature: 1In order to increase sales of advertised articlesAs a sellerI want buyers to be able to easily find ads for articles they wantto buyScenario: Searching by keyword and locationGiven Sally wants to buy a "puppy" for her sonWhen she looks for "puppy" in the "Pets and Animals" categoryThen she should obtain a list of "puppy" ads 35. 35Scenario: Searching by keyword and locationGiven Sally wants to buy a "puppy" for her sonWhen she looks for "puppy" in the "Pets and Animals" categoryThen she should obtain a list of "puppy" adsScenario: Searching by keyword and locationGiven Sally wants to buy a for her sonWhen she looks for in the categoryThen she should obtain a list of ads for for sale.Examples:|present|category |expected||puppy|Pets & Animals | puppies||kitten |Pets & Animals | kittens||seiko|Jewellery & Watches| watch | 36. 36Scenario: Searching by keyword and location 1Given Sally wants to buy a "puppy" for her sonWhen she looks for "puppy" in the "Pets and Animals" categoryThen she should obtain a list of "puppy" adsimport org.junit.runner.RunWith;2import cucumber.junit.Cucumber;@RunWith(Cucumber.class)@Cucumber.Options(format={"pretty", "html:target/cucumber"})public class RunTests {} 37. 37Scenario: Searching by keyword and location 1Given Sally wants to buy a "puppy" for her sonWhen she looks for "puppy" in the "Pets and Animals" categoryThen she should obtain a list of "puppy" adsimport org.junit.runner.RunWith; 2import cucumber.junit.Cucumber;public class SearchAdsSteps {@Steps@RunWith(Cucumber.class)3BuyerSteps buyer;@Cucumber.Options(format={"pretty", "html:target/cucumber"})public class RunTests { buy a "([^"]*)" for her son$")@Given("^Sally wants to} public void buyingAPresent(String present) {buyer.opens_home_page();}@When("^she looks for "([^"]*)" in the "([^"]*)" category$")public void adSearchByCategoryAndKeyword(String category, String keyword) {buyer.chooses_category_and_keywords(category, keyword);buyer.performs_search();}@Then("^she should obtain a list of "([^"]*)" ads$")public void shouldOnlySeeAdsContainingKeyword(String keyword) {buyer.should_only_see_results_with_titles_containing(keyword);}} 38. 38100% Groovy 39. 39search_by_keyword_and_location.story scenario "Searching by keyword and location", { given "Sally wants to buy a puppy for her son" when "she looks for puppy in the Pets and Animals category" then "she should obtain a list of ads for puppies for sale" } scenario "Searching by keyword and location", { given "Sally wants to buy a #present for her son" when "she looks for #present in the #category category" then "she should obtain a list of ads for #expected for sale" where "examples should be", { present = [puppy,kitten, seiko] category = [Pets & Animals,Pets & Animals, Jewellery & Watches] expected = [puppies, kittens,watch] } } 40. 40search_by_keyword_and_location.story scenario "Searching by keyword and location", { 1 given "Sally wants to buy a puppy for her son" when "she looks for puppy in the Pets and Animals category" then "she should obtain a list of ads for puppies for sale" } 41. 41search_by_keyword_and_location.story scenario "Searching by keyword and location", { 1 given "Sally wants to buy a puppy for her son" when "she looks for puppy in the Pets and Animals category" then "she should obtain a list of ads for puppies for sale" }using "thucydides" 2thucydides.uses_steps_from BuyerStepsscenario "Searching by keyword and location", {given "Sally wants to buy a puppy for her son", {buyer.opens_home_page()}when "she looks for puppy in the Pets and Animals category", {buyer.chooses_category_and_keywords(category, keyword);buyer.performs_search();}then "she should obtain a list of ads for puppies for sale",{ buyer.should_only_see_results_with_titles_containing keyword}} 42. 42Keeping an eye on things 43. 43(Think Two-CDs) 44. 441 Discover your acceptance criteria2 Automate your acceptance criteria3 Implement your acceptance criteria4 Execute your acceptance tests 45. 451Discover your acceptance criteriaFeature: Browse CatalogIn order to find items that I would like to buyAs a customerI want to be able to browse through the catalog Story: Browse by category In order to find items more easily As a customer I want to be able to browse through the product categoriesAcceptance CriteriaSee all the top-level categoriesBrowse through the category hierarchyShould display the correct products for each categoryEach category should have the correct sub-categoriesDene acceptance criteria for each story 46. 46 1 Discover your acceptance criteriaAcceptance CriteriaSee all the top-level categoriesBrowse through the category hierarchyShould display the correct products for each categoryEach category should have the correct sub-categories Scenario: See all top-level categories Given I want to browse the catalog When I am on the home page Then I should see the following product categories: Clothing, Accessories, Shoes Clarify the acceptance criteria with examples 47. 47(Do this together) 48. 48 2 Automate your acceptance criteriaStory: Browse by categoryIn order to find items more easily Acceptance CriteriaAs a customer top-level categoriesSee all theI want Browse through the category the product categories to be able to browse through hierarchyScenario: See all top-level categoriesShould display the correct products for each categoryGiven I want to browse the catalogEach category should have the correct sub-categoriesWhen I am on the home pageThen I should see the following product categories: Clothing, Accessories, ShoesNarrative:In order to find items more easilyAs a customerI want to be able to see what product categories existScenario: See all top-level categoriesGiven I want to browse the catalogWhen I am on the home pageThen I should see the following product categories: Clothing, Accessories, Shoes We now have an executable requirement 49. 49 2 Automate your acceptance criteria...but they will be reported as pending 50. 50 3Implement your acceptance criteriaNarrative:In order to find items more easilyAs a customerI want to be able to see what product categories existScenario: See all top-level categoriesGiven I want to browse the catalogWhen I am on the home pageThen I should see the following product categories: Clothing, Accessories, Shoes 51. 513 Implement your acceptance criteria 52. 523 Implement your acceptance criteria JUnit 53. 534 Execute your acceptance tests 54. 54 55. 55 56. 56 57. 57 58. 58 59. 59 60. From Acceptance Teststo Developer Tests 61. BDD - A Development Tool 62. TDD or BDD? 63. Make it passWrite a failing test TDD Refactor What test should I write? 64. Acceptance Tests (high level features)SpockDeveloper Tests(low level features)What features should I implement?etc. 65. Goal: In order to increase revenue from commissions on classified ads salesAs the head of the classified ads departmentI want to increase the number of items sold via our classified ads Story: In order to find the items I am interested in fasterAcceptance As a buyer I want to be able to list all the ads with a particular keyword in the description or title. Tests Scenario: Searching by keyword and location Scenario: Searching by keyword and locationScenario: Searching by keyword Given Sally wants to buy a apuppyfor her son Given Sally wants to buy apuppy for her sonGiven Sally wants to buy puppy for her son When she looks for ads ininthePets & Animals category containing puppy When she looks for ads inthe Pets & Animals category containing puppyWhen she looks for ads the Pets & Animals category containing puppy inThen she should obtain a list of ads for puppies for saleinNew South WalesNew South Walesclass WhenCalculatingGST extends Specification {class WhenCalculatingGST extends Specification {class WhenCalculatingGST extends Specification {def "GST should apply on ordinary articles"() {given: should apply on ordinary articles"() { {def "GST "we are selling a shirt"given: should apply on ordinary articles"()def "GST "we are selling a shirt" Developer def sale = Sale.of(1,"shirt").forANetPriceOf(10.00)def sale areSale.of(1,"shirt").forANetPriceOf(10.00)given: "we = selling a shirt"when: "we calculate the price including GST"def sale = Sale.of(1,"shirt").forANetPriceOf(10.00)def "we calculate sale.totalPricewhen: totalPrice = the price including GST"def "we calculate the price GST ofwhen: totalPrice including GST"then: "the totalPrice= =sale.totalPrice 10%"defprice should sale.totalPrice includetotalPrice == should include GST of 10%"then: "the price 11.00 include GST of 10%"then: "the price shouldtotalPrice == 11.00Tests}totalPrice == 11.00} }} }} 66. Unit Tests Acceptance tests 67. Spock - BDD for developers 68. Spockclass WhenCalculatingGST extends Specification {def "GST should apply on ordinary articles"() {given: "we are selling a shirt"def sale = Sale.of(1,"shirt").forANetPriceOf(10.00)when: "we calculate the price including GST"def totalPrice = sale.totalPricethen: "the price should include GST of 10%"totalPrice == 11.00}} Given-When-Then structure 69. Spockclass WhenCalculatingGST extends Specification {...def "GST should not apply on GST-exempt articles"() {given: "we are selling a bottle of milk"def sale = Sale.of(1,"shirt").forANetPriceOf(5.00)when: "we calculate the price including GST"def totalPrice = sale.totalPricethen: "the price should not include GST%"totalPrice == 5.00}}Meaningful error messages 70. SpockLightweight stubbingclass WhenCalculatingGST extends Specification {def "GST should apply on ordinary articles"() {given: "GST is at 12.5%"def gstRateProvider = Mock(GSTRateProvider)gstRateProvider.getRate() >> 0.125Sales sales = new Sales(gstRateProvider)and: "we are selling a shirt"def sale = sales.makeSaleOf(1,"shirt").forANetPriceOf(10.00)when: "we calculate the price including GST"def totalPrice = sale.totalPricethen: "the price should include GST of 12.5%"totalPrice == 11.25}} 71. Spockclass WhenDeliveringSoldItems extends Specification {def gstRateProvider = Mock(GSTRateProvider)def deliveryService = Mock(DeliveryService)def "Sold articles should be delivered"() {given: "we are selling shirts online"Sales sales = new Sales(gstRateProvider, deliveryService)when: "we sell a shirt"sales.makeSaleOf(1,"shirt").forANetPriceOf(10.00)then: "the shirt should be sent to the delivery service"1 * deliveryService.dispatch(_)}} Lightweight mocking 72. Spockclass WhenDisplayingTagNamesInAReadableForm extends Specification {def inflection = Inflector.instancedef "should transform singular nouns into plurals"() {when: "I find the plural form of a single word"def pluralForm = inflection.of(singleForm).inPluralForm().toString();then: "the plural form should be gramatically correct"pluralForm == expectedPluralFormwhere:singleForm| expectedPluralFormepic| epicsfeature | featuresstory | storiesstories | storiesoctopus | octopisheep | sheep}} Data-driven tests 73. Spock with Arquillianclass AccountServiceSpecification extends Specification { @Deployment def static JavaArchive "create deployment"() { return ShrinkWrap.create(JavaArchive.class) .addClasses(AccountService.class, Account.class, SecureAccountService.class) .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } @Inject AccountService service def "transferring between accounts should result in account withdrawl and deposit"() { when: service.transfer(from, to, amount) then:BDD-style integration tests from.balance == fromBalance to.balance == toBalance where: from > { totalPrice = sale.totalPrice } "Then the price should include a GST of 10%" >> { totalPrice === 11.00 } } var sale = Sale(); var totalPrice = 0.0} 76. class WhenCalculatingGST2 extends Specification with Mockito { sequential "GST should apply on ordinary articles" >> {Lightweight "Given we are selling a shirt" >> {stubbing DSL val sales = Sales(mock[GSTProvider]) sales.gstProvider.rate returns 12.5 sale = sales.makeSaleOf(1, "shirt").forANetPriceOf(10.00) } "When we calculate the price including GST" >> { totalPrice = sale.totalPrice } "Then the price should include a GST of 12.5%" >> { totalPrice === 11.25 } } var sale = Sale(); var totalPrice = 0.0} 77. class WhenDeliveringSoldItems extends Specification with Mockito { sequential "Sold articles should be delivered" >> { "Given we are selling shirts online" >> { sales = Sales(mock[GSTProvider], mock[DeliveryService]) } "When we sell a shirt" >> { sale = sales.makeSaleOf(1, "shirt").forANetPriceOf(10.00) } "Then the shirt should be sent to the delivery service" >> { there was one(sales.deliveryService).dispatch(anyString) } } Lightweight var sale = Sale(); var sales = Sales() mocking DSL} 78. class WhenDisplayingTagNamesInAReadableForm extends Specification with Tables { "The inflector should transform singular nouns into plurals" >> { """ when I find the plural form of a single word, then the plural form should be gramatically correct: """ >> { "single form" | "plural form" |> "epic" ! "epics" | "feature" ! "features" | Data-driven tests, "story" ! "story" | Scala-style "stories" ! "stories" | "octopus" ! "octopi" | "sheep" ! "sheep" | { (singleForm, pluralForm) => Inflection.of(singleForm).inPluralForm.toString === pluralForm } } }} 79. Jasmine - BDD for Javascript 80. describe( "temperature converter", function () {it("converts fahrenheit to celsius", function () {expect(Convert(50, "F").to("C")).toEqual(10);});});Simple assertion structure 81. describe( "temperature converter", function () {it("converts fahrenheit to celsius", function () {expect(Convert(50, "F").to("C")).toEqual(10);});it("converts celsius to fahrenheit", function () {expect(Convert(30, "C").to("F")).toEqual(86);});});More complex behavior 82. describe( "converter library", function () {describe( "temperature converter", function () {it("converts fahrenheit to celsius", function () {expect(Convert(50, "F").to("C")).toEqual(10);});it("converts celsius to fahrenheit", function () {expect(Convert(30, "C").to("F")).toEqual(86);});});describe( "weight converter", function () {it("converts kilograms to pounds", function () {expect(Convert(100, "KG").to("LB")).toEqual(220);});});});Nested behaviors 83. Lots of matchersit("is defined", function () { var name = "Andrew"; property defined expect(name).toBeDefined(); }) it("is true", function () { true or false expect(Lib.isAWeekDay()).toBeTruthy(); }); it("is less than 10", function () { expect(5).toBeLessThan(10); greater than or less than}); it("is greater than 10", function () { expect(20).toBeGreaterThan(10); }); it("should contain oranges", function () { expect(["apples", "oranges", "pears"]).toContain("oranges"); }); contains 84. And it works with Maven! 85. Evaluate test results in a browser 86. Evaluate test results in a browser 87. Generate JUnit-compatible results 88. hAp://try-jasmine.heroku.com/ 89. In conclusion...Its behavior allthe way down 90. Thank YouJohn Ferguson Smart