Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Behaviour Driven Development on iOS

Behaviour Driven Development on iOS

The what and why of BDD, and one way to implement it for an iOS project. iOS implementation using Frank (http://www.testingwithfrank.com) and Cedar (https://github.com/pivotal/cedar).

A talk I gave at the Christchurch (NZ) iDev meetup on Tuesday 27th March 2012.

Avatar for mark_haylock

mark_haylock

March 27, 2012
Tweet

More Decks by mark_haylock

Other Decks in Programming

Transcript

  1. Behaviour–Driven Development for iOS The what and why of BDD,

    and one way to implement it for an iOS project.
  2. The origin of Behaviour–Driven Development “I had a problem. While

    using and teaching agile practices like test-driven development (TDD) on projects in different environments, I kept coming across the same confusion and misunderstandings. Programmers wanted to know where to start, what to test and what not to test, how much to test in one go, what to call their tests, and how to understand why a test fails.” Introducing BDD – Dan North http://dannorth.net/introducing-bdd/
  3. BDD is a process that • Creates a place to

    start. • Helps determine what to test and when to test it.
  4. BDD is a process that • Creates a place to

    start. • Helps determine what to test and when to test it. • Provides a framework for articulating requirements.
  5. BDD is a process that • Creates a place to

    start. • Helps determine what to test and when to test it. • Provides a framework for articulating requirements. • Helps you sleep at night.
  6. Features • Written in plain english (or whichever language you

    choose). • Serve as acceptance criteria that can be read and written by stakeholders.
  7. Features • Written in plain english (or whichever language you

    choose). • Serve as acceptance criteria that can be read and written by stakeholders. • Plain executable english.
  8. Feature: (one line describing the feature) As a [role] I

    want [feature] So that [business benefit] In order to [business benefit] As a [role] I want [feature] Features (written in “Gherkin”)
  9. Scenarios (written in “Gherkin”) Scenario: (one line title describing this

    scenario) Given [context] When [event/action] Then [expected outcome] Scenario: Login is successful Given the app is launched And I am not logged in When I login Then I should see the start screen
  10. Feature + Scenarios (in “Gherkin”) Feature: Some terse yet descriptive

    text of what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Scenario: Some determinable business situation Given some precondition And some other precondition When some action by the actor And some other action And yet another action Then some testable outcome is achieved And something else we can check happens too Scenario: A different situation ... Example from https://github.com/cucumber/cucumber/wiki/Gherkin
  11. Feature + Scenarios Feature: Some terse yet descriptive text of

    what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Scenario: Some determinable business situation Given some precondition And some other precondition When some action by the actor And some other action And yet another action Then some testable outcome is achieved And something else we can check happens too Scenario: A different situation ... Example from https://github.com/cucumber/cucumber/wiki/Gherkin
  12. Step Definitions • Code that defines how each step in

    a Scenario is translated into executable code.
  13. Step Definitions • Code that defines how each step in

    a Scenario is translated into executable code. • Step definitions should treat the application as a black box and only test the same things that a real user of the application will see.
  14. Step Definitions • Code that defines how each step in

    a Scenario is translated into executable code. • Step definitions should treat the application as a black box and only test the same things that a real user of the application will see. • These can be quite slow to execute depending on how everything fits together (but it doesn’t matter).
  15. Step Definitions • Code that defines how each step in

    a Scenario is translated into executable code. • Step definitions should treat the application as a black box and only test the same things that a real user of the application will see. • These can be quite slow to execute depending on how everything fits together (but it doesn’t matter). • Cucumber is a popular BDD tool written in Ruby that understands the “Gherkin” syntax and gives you a framework to create step definitions.
  16. The anatomy of Cucumber project project_root/ | `-- features Slides

    from http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  17. The anatomy of Cucumber project project_root/ | `-- features |--

    awesomeness.feature |-- greatest_ever.feature Slides from http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  18. The anatomy of Cucumber project project_root/ | `-- features |--

    awesomeness.feature |-- greatest_ever.feature `-- support |-- env.rb `-- other_helpers.rb Slides from http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  19. The anatomy of Cucumber project project_root/ | `-- features |--

    awesomeness.feature |-- greatest_ever.feature `-- support |-- env.rb `-- other_helpers.rb |-- step_definitions | |-- domain_concept_A.rb | `-- domain_concept_B.rb Slides from http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  20. A Step Definition File Given /^I have (\d+) cucumbers in

    my belly$/ do |cukes| # Some Ruby code here end Given "I have $n cucumbers in my belly" do |cukes| # Some Ruby code here end # Both of these match 'Given I have 93 cucumbers in my belly' Examples from https://github.com/cucumber/cucumber/wiki/Step-Definitions
  21. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  22. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  23. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  24. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  25. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  26. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  27. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  28. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass 9. Unit Test is now Green, Refactor! http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  29. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass 9. Unit Test is now Green, Refactor! 10. Repeat Steps 6–9 until the Steps are Green. http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  30. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass 9. Unit Test is now Green, Refactor! 10. Repeat Steps 6–9 until the Steps are Green. 11. Step is now Green, Refactor! http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  31. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass 9. Unit Test is now Green, Refactor! 10. Repeat Steps 6–9 until the Steps are Green. 11. Step is now Green, Refactor! 12. Write the next Step definition (go back to 3 and repeat). http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  32. Outside–In 1. Write the Scenarios first 2. Steps are now

    Pending 3. Write the Step definition 4. Steps are now Red 5. Go down a level to a unit testing framework 6. Write Unit Test 7. Unit Test is now Red 8. Make the Unit Test pass 9. Unit Test is now Green, Refactor! 10. Repeat Steps 6–9 until the Steps are Green. 11. Step is now Green, Refactor! 12. Write the next Step definition (go back to 3 and repeat). 13. All Steps are green? Your Feature is complete! http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html
  33. How can you do this with an iOS app? A

    surprisingly large array of options: UI Testing (UIKit): UI Automation, UISpecRunner, FoneMonkey Frank, iCuke, Calabash-iOS (LessPainful.com), Zucchini Framework. Unit Testing: OCUnit/SenTestingKit, CATCH, GHUnit, Google Toolbox for Mac Kiwi, Cedar http://stackoverflow.com/questions/4114083/ios-tests-specs-tdd-bdd-and-integration-acceptance-testing
  34. How can you do this with an iOS app? A

    surprisingly large array of options: UI Testing (UIKit): UI Automation, UISpecRunner, FoneMonkey Frank, iCuke, Calabash-iOS (LessPainful.com), Zucchini Framework. Unit Testing: OCUnit/SenTestingKit, CATCH, GHUnit, Google Toolbox for Mac Kiwi, Cedar http://stackoverflow.com/questions/4114083/ios-tests-specs-tdd-bdd-and-integration-acceptance-testing
  35. Frank • http://www.testingwithfrank.com • Cucumber with step definitions in Ruby.

    • Embeds a web service in the app via a static library.
  36. Frank • http://www.testingwithfrank.com • Cucumber with step definitions in Ruby.

    • Embeds a web service in the app via a static library. • Primarily created for Simulator testing but has been used successfully on device.
  37. Frank • http://www.testingwithfrank.com • Cucumber with step definitions in Ruby.

    • Embeds a web service in the app via a static library. • Primarily created for Simulator testing but has been used successfully on device. • Called ‘Frank’ because it pulls together other open source solutions (i.e. Frankenstein). Uses Cucumber, UIQuery and KIF.
  38. Frank • Comes with many predefined steps that you can

    use in your own steps or scenarios. Some examples: When I type "([^\"]*)" into the "([^\"]*)" text field When I fill in "([^\"]*)" with "([^\"]*)" When I type "#{text_to_type}" into the "#{text_field}" When I fill in text fields as follows: Given the device is in (a )?landscape orientation Given the device is in (a )?portrait orientation When I simulate a memory warning Then I rotate to the "([^\"]*)" - left right When I touch "([^\"]*)" When I touch "([^\"]*)" if exists When I touch the first table cell When I touch the table cell marked "([^\"]*)" http://www.testingwithfrank.com/supplied_steps.html
  39. Frank • Comes with many predefined steps that you can

    use in your own steps or scenarios. Some examples: When I touch the (\d*)(?:st|nd|rd|th)? table cell Then I touch the following: When I touch the button marked "([^\"]*)" When I touch the "([^\"]*)" action sheet button When I touch the (\d*)(?:st|nd|rd|th)? action sheet button When I touch the (\d*)(?:st|nd|rd|th)? alert view button When I flip switch "([^\"]*)" on Then I wait to see "([^\"]*)" Then I wait to not see "([^\"]*)" Then I wait to see a navigation bar titled "([^\"]*)" Then I wait to not see a navigation bar titled "([^\"]*)" Then I navigate back When I quit the simulator http://www.testingwithfrank.com/supplied_steps.html
  40. Imperative vs Declarative Steps • Imperative: Scenario: Changing email Given

    I am logged in And I am on the homepage When I click on "Account" And I fill in "email" with "[email protected]" And I press "Change Email" Then I should see "Email successfully changed" When I visit "My profile page" Then I should see "[email protected]" http://renderedtext.com/blog/2011/12/06/sucking-less-at-writing-cucumber/
  41. Imperative vs Declarative Steps • Imperative: Scenario: Changing email Given

    I am logged in And I am on the homepage When I click on "Account" And I fill in "email" with "[email protected]" And I press "Change Email" Then I should see "Email successfully changed" When I visit "My profile page" Then I should see "[email protected]" • Declarative: Background: Given I am logged in Scenario: Changing Email Given I am on my account page When I change my email Then I should see that my email has changed http://renderedtext.com/blog/2011/12/06/sucking-less-at-writing-cucumber/
  42. Frank Step Definitions When /^I touch the alert view button

    marked "([^\"]+)"$/ do |mark| touch( "alertView button marked:'#{mark}'" ) end Then /^I should see (\d+) apples$/ do |count| apples = frankly_map( "label marked:'red apples'", 'tag' ) apples.count.should == count.to_i end http://www.testingwithfrank.com/user_steps.html
  43. Frank • Can execute Objective-C methods on any view it

    can target. • Can execute methods on the AppDelegate, great for setting up an initial state.
  44. Frank • Can execute Objective-C methods on any view it

    can target. • Can execute methods on the AppDelegate, great for setting up an initial state. • Slow (often waiting for animations to complete).
  45. Frank • Can execute Objective-C methods on any view it

    can target. • Can execute methods on the AppDelegate, great for setting up an initial state. • Slow (often waiting for animations to complete). • Maintaining a separate target is annoying.
  46. Frank • Can execute Objective-C methods on any view it

    can target. • Can execute methods on the AppDelegate, great for setting up an initial state. • Slow (often waiting for animations to complete). • Maintaining a separate target is annoying. • Great community, very active involvement from creator Pete Hodgsen.
  47. Cedar • https://github.com/pivotal/cedar • RSpec style testing in Objective-C. •

    Very very fast. • Uses the OCMock Framework (if you wish) for mocks and stubs.
  48. Cedar • https://github.com/pivotal/cedar • RSpec style testing in Objective-C. •

    Very very fast. • Uses the OCMock Framework (if you wish) for mocks and stubs. • Now supports integration with OCUnit - which means Xcode integration and debugger support without a separate target.
  49. RSpec vs Cedar # bowling_spec.rb require 'bowling' describe Bowling, "#score"

    do it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should eq(0) end end #import "SpecHelper.h" using namespace Cedar::Matchers; SPEC_BEGIN(BowlingSpec) describe(@"Bowling#score", ^{ it(@"returns 0 for all gutter game", ^{ Bowling *bowling = [[Bowling alloc] init]; for(int i = 0; i < 20; i++) { [bowling hit:0]; } bowling.score should equal(0); }); }); SPEC_END Ruby example from http://rspec.info/
  50. Growing a Spec #import "SpecHelper.h" using namespace Cedar::Matchers; SPEC_BEGIN(BowlingSpec) describe(@"Bowling#score",

    ^{ __block Bowling *bowling; beforeEach(^{ bowling = [[Bowling alloc] init]; }); it(@"returns 0 for all gutter game", ^{ for(int i = 0; i < 20; i++) { [bowling hit:0]; } bowling.score should equal(0); }); it(@"returns 300 for all strikes game", ^{ for(int i = 0; i < 12; i++) { [bowling hit:10]; } bowling.score should equal(300); }); }); SPEC_END
  51. My experience of Frank & Cedar • Setting up the

    toolset for productive work can take a while.
  52. My experience of Frank & Cedar • Setting up the

    toolset for productive work can take a while. • Still bugs and room for improvement especially with Frank.
  53. My experience of Frank & Cedar • Setting up the

    toolset for productive work can take a while. • Still bugs and room for improvement especially with Frank. • Frank community busy and responsive, Cedar very quiet (but framework seems more mature).
  54. My experience of Frank & Cedar • Setting up the

    toolset for productive work can take a while. • Still bugs and room for improvement especially with Frank. • Frank community busy and responsive, Cedar very quiet (but framework seems more mature). • Many adventures yet to be had.
  55. Links and Suggested Reading • http://www.testingwithfrank.com/ • https://github.com/pivotal/cedar • http://pivotallabs.com/blabs/categories/cedar

    • http://cukes.info/ • http://ocmock.org/ • http://dannorth.net/introducing-bdd/ • http://benmabey.com/tags/cucumber/ • https://vimeo.com/channels/291403 “The RSpec Book” Behaviour–Driven Development with RSpec, Cucumber, and Friends. http://pragprog.com