Bdd state-of-the-union (Devoxx and XPDays version)

2,188 views
2,085 views

Published on

2 Comments
9 Likes
Statistics
Notes
No Downloads
Views
Total views
2,188
On SlideShare
0
From Embeds
0
Number of Embeds
104
Actions
Shares
0
Downloads
61
Comments
2
Likes
9
Embeds 0
No embeds

No notes for slide

Bdd state-of-the-union (Devoxx and XPDays version)

  1. 1. Behavior-Driven Development on the JVM A State of the Union John  Ferguson  Smart
  2. 2. Consulta nt Trainer Mentor Author Speaker CoderJohn Fer guson S mar t
  3. 3. What is BDD?
  4. 4. Business Analyst Common LanguageBusiness Developer Tester
  5. 5. ExecutableSpecifications
  6. 6. Ou ts id e In
  7. 7. Value Driven
  8. 8. So why use BDD?Only build features that add real valueLess wasted effortBetter communicationHigher quality, better tested productTraceability
  9. 9. BDD - Requirements Analysis and Communication
  10. 10. Goals Capabilities Features StoriesExamples/ScenariosAcceptance Criteria
  11. 11. 11 Successful projects start with a shared vision“We are going to build an online classifieds website”
  12. 12. 12You define goals to achieve your vision “We can increase advertising revenue by letting sellers post their classified ads online” “Let’s get more sales for our advertisers by making the ads easier to find online.”
  13. 13. Determining the value of a goal A good goal should add value to the business Increase revenue Reduce costs Avoid future costs Protect revenue “Increase advertising revenue by allowing sellers to post classified ads online” “Reduce the costs involved in publishing a classified 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. 14. What does the customer really need? I want users to be able to search for products by keywordWhy? So that potential buyers can find the articles they want Why? So that our sellers can sell their stuff faster Why? 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. 15. What does the customer really need? Good teams push back! Users tend to express requirements as implementations We need to find the business need behind the suggested implementation I want users to be able to search by keyword So in order to make the site more attractive for sellers Buyers need to be able to find things easily A search feature might be one way to achieve this But full-text searches might be more effective than keywords
  16. 16. Features and capabilities help deliver these goals “Let’s get more sales for our advertisers by making the ads easier to find online.” Notify potential buyers about new items In Search for online of advertised articles order to increase sales ads As aorder to increase sales of advertised articles In seller I want previous buyers to know about new items As a seller that theybuyers be interested in buying ads for I want might to be able to easily find articles they want to buy
  17. 17. Feature Injection - what features do you do first? Our goals say what business value we need to deliver We implement the minimum features required to deliver this business value Search for online ads The goal comes first In order to increase sales of advertised articles The stakeholder is As a seller secondary I want buyers to be able to easily find ads for articles they want to buy The feature must be required to achieve the goal
  18. 18. 18We use examples and stories to explore the featuresSearch for online ads“Searching by category” “Searching by keyword and category”
  19. 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. 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 Keywords puppy Pets & Animals labrador kitten Pets & Animals burmese kitten Toys fluffy cat Acceptance Criteria illustrate and validate the stories
  21. 21. Organize your requirements
  22. 22. Requirements can come from many sources...
  23. 23. Requirements can come from many sources...
  24. 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 items As 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. 25. 25BDD - Test Automation and Beyond
  26. 26. 26The original Java BDD framework
  27. 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. 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 <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. Examples: |present |category |expected| |puppy |Pets & Animals | puppies| |kitten |Pets & Animals | kittens| |seiko |Jewellery & Watches| watch |
  29. 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. 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 {     @Steps 2     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. 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 {     @Steps 2     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<String> storyPaths() {         String codeLocation = codeLocationFromClass(this.getClass()).getFile();     }         return new StoryFinder().findPaths(codeLocation, asList("**/*.story", }                 "**/traders_can_be_subset.story"), asList(""), "file:" + codeLocation);     } }
  32. 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 {     @Steps 2     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. 33. 33Now available in JVM flavor!
  34. 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. 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" ads 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. Examples: |present |category |expected| |puppy |Pets & Animals | puppies| |kitten |Pets & Animals | kittens| |seiko |Jewellery & Watches| watch |
  36. 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. 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) 3     BuyerSteps 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. 38. 38100% Groovy
  39. 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. 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. 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" 2 thucydides.uses_steps_from BuyerSteps scenario "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. 42. 42Keeping an eye on things
  43. 43. 43(Think “Two-CDs”)
  44. 44. 441 Discover your acceptance criteria 2 Automate your acceptance criteria 3 Implement your acceptance criteria 4 Execute your acceptance tests
  45. 45. 45 1 Discover 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 categories Acceptance Criteria See all the top-level categories Browse through the category hierarchy Should display the correct products for each category Each category should have the correct sub-categories Define acceptance criteria for each story
  46. 46. 46 1 Discover your acceptance criteriaAcceptance Criteria See all the top-level categories Browse through the category hierarchy Should display the correct products for each category Each 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. 47(Do this together)
  48. 48. 48 2 Automate your acceptance criteria Story: Browse by category In order to find items more easily Acceptance Criteria As a customer top-level categories See all the I want Browse through the category the product categories to be able to browse through hierarchy Scenario: See all top-level categories Should display the correct products for each category Given I want to browse the catalog Each category should have the correct sub-categories When I am on the home page Then 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. 49 2 Automate your acceptance criteria...but they will be reported as ‘pending’
  50. 50. 50 3 Implement 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. 51. 513 Implement your acceptance criteria
  52. 52. 523 Implement your acceptance criteria JUnit
  53. 53. 534 Execute your acceptance tests
  54. 54. 54
  55. 55. 55
  56. 56. 56
  57. 57. 57
  58. 58. 58
  59. 59. 59
  60. 60. From Acceptance Tests to Developer Tests
  61. 61. BDD - A Development Tool
  62. 62. TDD or BDD?
  63. 63. Make it passWrite a failing test TDD Refactor What test should I write?
  64. 64. Acceptance Tests (high level features) Spock Developer Tests (low level features) What features should I implement?etc.
  65. 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 faster Acceptance   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 location Scenario: Searching by keyword Given Sally wants to buy a apuppyfor her son Given Sally wants to buy apuppy for her son Given 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 puppy When she looks for ads the Pets & Animals category containing puppy inThen she should obtain a list of ads for puppies for sale inNew South Wales New South Wales class 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.totalPrice         when: totalPrice = the price including GST"             def "we calculate the price GST of         when: totalPrice including GST"         then: "the totalPrice= =sale.totalPrice 10%"             def price should sale.totalPrice include             totalPrice == should include GST of 10%"         then: "the price 11.00 include GST of 10%"         then: "the price should             totalPrice == 11.00 Tests     }             totalPrice == 11.00 }     } }     } }
  66. 66. Unit Tests Acceptance tests
  67. 67. Spock - BDD for developers
  68. 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.totalPrice        then: "the price should include GST of 10%"            totalPrice == 11.00    }} Given-When-Then structure
  69. 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.totalPrice        then: "the price should not include GST%"            totalPrice == 5.00    }} Meaningful error messages
  70. 70. Spock Lightweight stubbingclass WhenCalculatingGST extends Specification {    def "GST should apply on ordinary articles"() {        given: "GST is at 12.5%"            def gstRateProvider = Mock(GSTRateProvider)            gstRateProvider.getRate() >> 0.125            Sales 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.totalPrice        then: "the price should include GST of 12.5%"            totalPrice == 11.25    }}
  71. 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. 72. Spockclass WhenDisplayingTagNamesInAReadableForm extends Specification {    def inflection = Inflector.instance    def "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 == expectedPluralForm        where:            singleForm | expectedPluralForm            epic | epics            feature | features            story | stories            stories | stories            octopus | octopi            sheep | sheep    }} Data-driven tests
  73. 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  <<                  [new  Account(100),    new  Account(10)]                to  <<                      [new  Account(50),      new  Account(90)]                amount  <<              [50,                                10]                fromBalance  <<    [50,                                0]                toBalance  <<        [100,                              100]        }}
  74. 74. Spec2 - BDD for Scala
  75. 75. class  WhenCalculatingGST  extends  Specification  {  sequential    "GST  should  apply  on  ordinary  articles"  >>  {        "Given  we  are  selling  a  shirt"  >>  {            sale  =  Sale.of(1,  "shirt").forANetPriceOf(10.00)        }        "When  we  calculate  the  price  including  GST"  >>  {            totalPrice  =  sale.totalPrice        }        "Then  the  price  should  include  a  GST  of  10%"  >>  {            totalPrice  ===  11.00        }    }    var  sale  =  Sale();  var  totalPrice  =  0.0}
  76. 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. 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. 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. 79. Jasmine - BDD for Javascript
  80. 80. describe( "temperature converter", function () {    it("converts fahrenheit to celsius", function () {        expect(Convert(50, "F").to("C")).toEqual(10);    });}); Simple assertion structure
  81. 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. 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. 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. 84. And it works with Maven!
  85. 85. Evaluate test results in a browser
  86. 86. Evaluate test results in a browser
  87. 87. Generate JUnit-compatible results
  88. 88. hAp://try-­‐jasmine.heroku.com/
  89. 89. In conclusion...It’s behavior allthe way down
  90. 90. Thank You John  Ferguson  Smart

×