BDD FROM THE
                   Luismi Cavallé
TRENCHES     Jorge Gómez Sancha
                            BeBanjo
As a [Role]
I want [Feature]
So that [Benefit]

Scenario = Acceptance Criteria

  Given [Context]
  And [Some more Context...
Given [Context]   Planteamiento



When [Event]      Nudo



Then [Outcome]    Desenlace
User Stories
                  =
Great way to communicate functionality
Searching for “THE WAY” to develop software
We have made good progress...
...but we are always searching
HOW
HOW
  H
  Y
Friday
(afternoon)
What’s the most
important thing the
 system doesn’t do?


              http://dannorth.net/introducing-bdd
Feature
       Feature
                                  Feature
                                                       Fe...
Feature
  Feature
  Feature
  Feature
   Feature
    Feature
     Feature
Story: User sees timeline for a title

  As a user,
  I want to see a timeline of all the events associated to a title,
  ...
It’s game time
Would you mind giving me some privacy?
Would you mind giving me some privacy?
                      Porras



LuisMi
Estimates


Features   L
           uismi   R J
                    ai     orge   S   ergio


  A        3       2       4...
Estimates             We use these


Features   L
           uismi   R J
                    ai     orge   S   ergio   Ave...
Burndown Junkies
Functionality
        Left




                 F          M   T   W   Th

                     Time
Black Monday
Progress               Ideal Preso


✓ Estimating, Game, Burndown   20     100

  Pair Programming             20
        ...
Monday
“Pair all the fucking time”
                  -- Obie Fernandez
“Aparéate todo el puto día”
                     -- BeBanjo
“Aparéate todo el puto día”
                     -- BeBanjo
# /bebanjo_app/features/login.feature




Scenario: User logs in successfully
  Given an existing user Robert with passwor...
$ cd bebanjo_app
$ cd bebanjo_app
$ script/cucumber features/login.feature
$ cd bebanjo_app
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user R...
# /bebanjo_app/features/steps/user_steps.rb
# /bebanjo_app/features/steps/user_steps.rb




Given /^an existing user (.*) with password '(.*)'$/ do |username, passwor...
# /bebanjo_app/features/steps/user_steps.rb




Given /^an existing user (.*) with password '(.*)'$/ do |username, passwor...
# /bebanjo_app/features/steps/user_steps.rb




Given /^an existing user (.*) with password '(.*)'$/ do |username, passwor...
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user Robert with passwo...
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user Robert with passwo...
$ script/generate model User username:string password:string
$ script/generate model User username:string password:string

$ rake db:migrate && rake db:test:prepare
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature

Scenario: User logs in successfully
  Given an existing user Robert with passwor...
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|...
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|...
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|...
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|...
# /bebanjo_app/features/steps/user_steps.rb




When /^(.*) logs in entering the password '(.*)'$/ do |username, password|...
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature

Scenario: User logs in successfully
  Given an existing user Robert with passwor...
$ script/cucumber features/login.feature

Scenario: User logs in successfully
  Given an existing user Robert with passwor...
# /bebanjo_app/config/routes.rb




       ActionController::Routing::Routes.draw do |map|
         map.resource :session
# /bebanjo_app/app/controllers/sessions_controller.rb
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
        de...
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
        de...
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
        de...
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
        de...
# /bebanjo_app/app/controllers/sessions_controller.rb



      class SessionsController < ApplicationController
        de...
# /bebanjo_app/app/views/sessions/new.html.erb


   <% form_for :session, :url => session_path do |f| -%>
   <p>
     <%= ...
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature

Scenario: User logs in successfully
  Given an existing user Robert with passwor...
# /bebanjo_app/features/steps/user_steps.rb




 Then /^(?:he|she) should be redirected to the home page$/ do
   response....
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user Robert with passwo...
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user Robert with passwo...
# /bebanjo_app/app/controllers/sessions_controller.rb


      class SessionsController < ApplicationController
        def...
$ script/cucumber features/login.feature
$ script/cucumber features/login.feature


Scenario: User logs in successfully
  Given an existing user Robert with passwo...
# /bebanjo_app/features/login.feature




   Scenario: User logs in with incorrect credentials
       Given an existing us...
$ git commit -a -m “Scenario: User logs in successfully”
$ git commit -a -m “Scenario: User logs in successfully”

$ git push origin master
Story driven

Swapping Criteria
Increased Focus

Knowledge share

  Pride Effect
Progress               Ideal Preso


✓ Estimating, Game, Burndown   20     100

✓ Pair Programming             20
        ...
Tuesday
Acceptance   Unit
Acceptance   Unit
Acceptance   Unit
Acceptance                      Unit


                         Development / Design
Great verification tool
              ...
Acceptance
# user_steps.rb

Given /^an existing user (.*)$/ do |username|
  create_user(:username => username)
end
# user_steps.rb

Given /^an existing user (.*)$/ do |username|
  create_user(:username => username)
end




# example_data...
# user_steps.rb

When /^(.*) logs in$/ do |username|
  visits quot;/session/newquot;
  fills_in quot;Usernamequot;, :with ...
# user_steps.rb

Then /^the email of (.*) should be (.*)/ do |username, email|
  user = User.find_by_username(username)
  ...
# user_steps.rb

 Then /^the email of (.*) should be (.*)/ do |username, email|
   user = User.find_by_username(username)
...
Acceptance testing is great...
Acceptance testing is great...
  • as a verification tool
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Acceptance testing is great...
  • as a verification tool
  • as a bridge between user needs and automated
     tests
     ...
Unit
RSpec offers lots of possibilities:
RSpec offers lots of possibilities:


 Model specs
Controller specs
  View specs
 Helper specs
 Routes specs
RSpec offers lots of possibilities:


 Model specs
Controller specs               Interaction-based
  View specs          ...
But we’re only interested in:

 Maximize Productivity
  Maximize Quality
So, how do we spec...

                        Our Views?
So, how do we spec...

                        Our Views?

         We mostly don’t
So, how do we spec...

                        Our Views?

         We mostly don’t

         Redundant with “then” steps ...
So, how do we spec...

             Our Controllers?
So, how do we spec...

             Our Controllers?

                  67% Coverage
So, how do we spec...

             Our Controllers?

                  67% Coverage

                  Interaction-based ...
So, how do we spec...

             Our Controllers?

                  67% Coverage

                  Interaction-based ...
# tasks_controller_spec.rb

describe quot;POST 'create'quot; do

   before(:each) do
    @task_attibutes = stub(Hash)
    ...
So, how do we spec...

                Our Models?
So, how do we spec...

                Our Models?
                   87% Coverage
So, how do we spec...

                Our Models?
                   87% Coverage

                   State-based functio...
So, how do we spec...

                Our Models?
                   87% Coverage

                   State-based functio...
describe ScheduledTitle do

 describe quot;late?quot; do

   it quot;should be late if begins_at is in the pastquot; do
  ...
So, how do we spec...

               Other objects?
So, how do we spec...

               Other objects?
              Helpers -- 32.1% Coverage
So, how do we spec...

               Other objects?
              Helpers -- 32.1% Coverage

              Routes -- never
So, how do we spec...

               Other objects?
              Helpers -- 32.1% Coverage

              Routes -- neve...
How do we not spec?
describe User do

          it { Factory(:user).should have_many(:projects) }
          it { Factory(:user).should belong_...
describe User do

          it { Factory(:user).should have_many(:projects) }
          it { Factory(:user).should belong_...
describe User do

          it { Factory(:user).should have_many(:projects) }
          it { Factory(:user).should belong_...
Progress               Ideal Preso


✓ Estimating, Game, Burndown   20     100

✓ Pair Programming             20
        ...
Wednesday
Playboy TV
Progress               Ideal Preso


✓ Estimating, Game, Burndown   20     100

✓ Pair Programming             20
        ...
Thursday
DRY




Less code         Skinny controller, fat model
Technical debt

                 DRY




   Less code           Skinny controller, fat model
Technical debt            Broken windows

                 DRY




   Less code           Skinny controller, fat model
Technical debt            Broken windows

                 DRY




   Less code           Skinny controller, fat model

  ...
Technical debt              Broken windows

                     DRY




     Less code             Skinny controller, fat...
Technical debt              Broken windows

                     DRY


          Refactoring towards
           a deeper i...
Progress               Ideal Preso


✓ Estimating, Game, Burndown   20     100

✓ Pair Programming             20
        ...
and again, Friday
You call that productivity?
The End
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Bdd From The Trenches
Upcoming SlideShare
Loading in …5
×

Bdd From The Trenches

3,094 views

Published on

At BeBanjo we develop web applications to manage all aspects of TV operations. Our development process is driven by User Stories, is divided in short iterations, implemented in pairs, integrating continuously, specifying behaviour and we use Ruby on Rails.

In this talk we want to share with the community how we do it; we will talk about technologies like Cucumber, RSpec, Selenium, Webrat or Cruisecontrol, but also about behaviour driven development, pair programming, beautiful code and above else about why we choose these practices over others, what business value they provide and why we think they make us more efficient and ultimately, happier.

All of this not from a theoric point of view, but rather straight from the trenches, sharing our experience and our itches, internal debates that we constantly have in pursuit of excellence.

Published in: Technology
1 Comment
18 Likes
Statistics
Notes
No Downloads
Views
Total views
3,094
On SlideShare
0
From Embeds
0
Number of Embeds
29
Actions
Shares
0
Downloads
0
Comments
1
Likes
18
Embeds 0
No embeds

No notes for slide

Bdd From The Trenches

  1. 1. BDD FROM THE Luismi Cavallé TRENCHES Jorge Gómez Sancha BeBanjo
  2. 2. As a [Role] I want [Feature] So that [Benefit] Scenario = Acceptance Criteria Given [Context] And [Some more Context] When [Event] And [Some other Event] Then [Outcome] And [Another Outcome]
  3. 3. Given [Context] Planteamiento When [Event] Nudo Then [Outcome] Desenlace
  4. 4. User Stories = Great way to communicate functionality
  5. 5. Searching for “THE WAY” to develop software
  6. 6. We have made good progress...
  7. 7. ...but we are always searching
  8. 8. HOW
  9. 9. HOW H Y
  10. 10. Friday (afternoon)
  11. 11. What’s the most important thing the system doesn’t do? http://dannorth.net/introducing-bdd
  12. 12. Feature Feature Feature Feature Feature Feature Feature Feature Feature Feature Feature Feature Feature Feature
  13. 13. Feature Feature Feature Feature Feature Feature Feature
  14. 14. Story: User sees timeline for a title As a user, I want to see a timeline of all the events associated to a title, So that I have access to its full history Scenario: An entry in the title timeline gets created when scheduled Given Telefonica operates VOD services Imagenio And a user Borat from the company Telefonica And Pedro scheduled the title Spaceballs on 15-03-2008 in Imagenio When Borat logs in And Borat goes to the title's timeline page for title Spaceballs Then he should see an entry 'Scheduled by Borat' on March 15, 2008
  15. 15. It’s game time
  16. 16. Would you mind giving me some privacy?
  17. 17. Would you mind giving me some privacy? Porras LuisMi
  18. 18. Estimates Features L uismi R J ai orge S ergio A 3 2 4 3 B 1 .5 2 1 C 1 .5 15 . 1 D 15 . 1 15 . 2
  19. 19. Estimates We use these Features L uismi R J ai orge S ergio Average A 3 2 4 3 3 B 1 .5 2 1 11 . C 1 .5 15 . 1 1 D 15 . 1 15 . 2 15 .
  20. 20. Burndown Junkies Functionality Left F M T W Th Time
  21. 21. Black Monday
  22. 22. Progress Ideal Preso ✓ Estimating, Game, Burndown 20 100 Pair Programming 20 75 Acceptance vs Unit 20 50 Upfront Design 10 25 Love 10 0 Productivity 20 40’ 35’ 30’ 25’ 20’ 15’ 10’ 5’ End! Total 100 Left: 80
  23. 23. Monday
  24. 24. “Pair all the fucking time” -- Obie Fernandez
  25. 25. “Aparéate todo el puto día” -- BeBanjo
  26. 26. “Aparéate todo el puto día” -- BeBanjo
  27. 27. # /bebanjo_app/features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' Then he should be redirected to the home page
  28. 28. $ cd bebanjo_app
  29. 29. $ cd bebanjo_app $ script/cucumber features/login.feature
  30. 30. $ cd bebanjo_app $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' [PENDING] When Robert logs in entering the password 'r0b3rt' [PENDING] Then he should be redirected to the home page [PENDING]
  31. 31. # /bebanjo_app/features/steps/user_steps.rb
  32. 32. # /bebanjo_app/features/steps/user_steps.rb Given /^an existing user (.*) with password '(.*)'$/ do |username, password|
  33. 33. # /bebanjo_app/features/steps/user_steps.rb Given /^an existing user (.*) with password '(.*)'$/ do |username, password| User.create(:username => username, :password => password)
  34. 34. # /bebanjo_app/features/steps/user_steps.rb Given /^an existing user (.*) with password '(.*)'$/ do |username, password| User.create(:username => username, :password => password) end
  35. 35. $ script/cucumber features/login.feature
  36. 36. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' [FAILED] uninitialized constant User (NameError) When Robert logs in entering the password 'r0b3rt' [PENDING] Then he should be redirected to the home page [PENDING]
  37. 37. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' [FAILED] uninitialized constant User (NameError) When Robert logs in entering the password 'r0b3rt' [PENDING] Then he should be redirected to the home page [PENDING]
  38. 38. $ script/generate model User username:string password:string
  39. 39. $ script/generate model User username:string password:string $ rake db:migrate && rake db:test:prepare
  40. 40. $ script/cucumber features/login.feature
  41. 41. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' [PENDING] Then he should be redirected to the home page [PENDING]
  42. 42. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password|
  43. 43. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password| visits quot;/session/newquot;
  44. 44. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password| visits quot;/session/newquot; fills_in quot;Usernamequot;, :with => username
  45. 45. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password| visits quot;/session/newquot; fills_in quot;Usernamequot;, :with => username fills_in quot;Passwordquot;, :with => password
  46. 46. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password| visits quot;/session/newquot; fills_in quot;Usernamequot;, :with => username fills_in quot;Passwordquot;, :with => password clicks_button quot;Sign inquot;
  47. 47. # /bebanjo_app/features/steps/user_steps.rb When /^(.*) logs in entering the password '(.*)'$/ do |username, password| visits quot;/session/newquot; fills_in quot;Usernamequot;, :with => username fills_in quot;Passwordquot;, :with => password clicks_button quot;Sign inquot; end
  48. 48. $ script/cucumber features/login.feature
  49. 49. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' [FAILED] No route matches quot;/session/newquot; with {:method=>:get} (RoutingError) Then he should be redirected to the home page [PENDING]
  50. 50. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' [FAILED] No route matches quot;/session/newquot; with {:method=>:get} (RoutingError) Then he should be redirected to the home page [PENDING]
  51. 51. # /bebanjo_app/config/routes.rb ActionController::Routing::Routes.draw do |map| map.resource :session
  52. 52. # /bebanjo_app/app/controllers/sessions_controller.rb
  53. 53. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController
  54. 54. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new
  55. 55. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end
  56. 56. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create
  57. 57. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create end
  58. 58. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create end end
  59. 59. # /bebanjo_app/app/views/sessions/new.html.erb <% form_for :session, :url => session_path do |f| -%> <p> <%= f.label :username %>: <%= f.text_field :username %> </p> <p> <%= f.label :password %>: <%= f.password_field :password %> </p> <p> <%= f.submit quot;Sign inquot; %> </p> <% end -%>
  60. 60. $ script/cucumber features/login.feature
  61. 61. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' Then he should be redirected to the home page [PENDING]
  62. 62. # /bebanjo_app/features/steps/user_steps.rb Then /^(?:he|she) should be redirected to the home page$/ do response.request.path.should == quot;/quot; end
  63. 63. $ script/cucumber features/login.feature
  64. 64. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' Then he should be redirected to the home page [FAILED] expected redirect to quot;/quot;, got no redirect (ExpectationNotMetError)
  65. 65. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' Then he should be redirected to the home page [FAILED] expected redirect to quot;/quot;, got no redirect (ExpectationNotMetError)
  66. 66. # /bebanjo_app/app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create redirect_to root_path end end
  67. 67. $ script/cucumber features/login.feature
  68. 68. $ script/cucumber features/login.feature Scenario: User logs in successfully Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'r0b3rt' Then he should be redirected to the home page 3 steps passed
  69. 69. # /bebanjo_app/features/login.feature Scenario: User logs in with incorrect credentials Given an existing user Robert with password 'r0b3rt' When Robert logs in entering the password 'robert' Then he should not be redirected to the home page
  70. 70. $ git commit -a -m “Scenario: User logs in successfully”
  71. 71. $ git commit -a -m “Scenario: User logs in successfully” $ git push origin master
  72. 72. Story driven Swapping Criteria
  73. 73. Increased Focus Knowledge share Pride Effect
  74. 74. Progress Ideal Preso ✓ Estimating, Game, Burndown 20 100 ✓ Pair Programming 20 75 Acceptance vs Unit 20 50 Upfront Design 10 25 Love 10 0 Productivity 20 40’ 35’ 30’ 25’ 20’ 15’ 10’ 5’ End! Total 100 Left: 60
  75. 75. Tuesday
  76. 76. Acceptance Unit
  77. 77. Acceptance Unit
  78. 78. Acceptance Unit
  79. 79. Acceptance Unit Development / Design Great verification tool tool
  80. 80. Acceptance
  81. 81. # user_steps.rb Given /^an existing user (.*)$/ do |username| create_user(:username => username) end
  82. 82. # user_steps.rb Given /^an existing user (.*)$/ do |username| create_user(:username => username) end # example_data.rb (fixture_replacement) attributes_for :user do |u| u.username = String.random u.password = quot;secretquot; u.password_confirmation = quot;secretquot; u.company = default_company end
  83. 83. # user_steps.rb When /^(.*) logs in$/ do |username| visits quot;/session/newquot; fills_in quot;Usernamequot;, :with => username fills_in quot;Passwordquot;, :with => quot;secretquot; clicks_button quot;Sign inquot; end
  84. 84. # user_steps.rb Then /^the email of (.*) should be (.*)/ do |username, email| user = User.find_by_username(username) user.email.should == email # assert_equal(email, user.email) end
  85. 85. # user_steps.rb Then /^the email of (.*) should be (.*)/ do |username, email| user = User.find_by_username(username) user.email.should == email # assert_equal(email, user.email) end Then /^the user should see the title (.*)/ do |title| response.should have_tag(quot;.titlequot;, title) # assert_select(quot;.titlequot;, title) end
  86. 86. Acceptance testing is great...
  87. 87. Acceptance testing is great... • as a verification tool
  88. 88. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests
  89. 89. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool....
  90. 90. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool.... • it is not easy to debug
  91. 91. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool.... • it is not easy to debug • it is slow to run
  92. 92. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool.... • it is not easy to debug • it is slow to run • it doesn’t encourage good design
  93. 93. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool.... • it is not easy to debug • it is slow to run • it doesn’t encourage good design • it is not comprehensive
  94. 94. Acceptance testing is great... • as a verification tool • as a bridge between user needs and automated tests ...but as a development tool.... • it is not easy to debug • it is slow to run • it doesn’t encourage good design • it is not comprehensive ...so we need something more
  95. 95. Unit
  96. 96. RSpec offers lots of possibilities:
  97. 97. RSpec offers lots of possibilities: Model specs Controller specs View specs Helper specs Routes specs
  98. 98. RSpec offers lots of possibilities: Model specs Controller specs Interaction-based View specs State-based Helper specs Structure-based Routes specs
  99. 99. But we’re only interested in: Maximize Productivity Maximize Quality
  100. 100. So, how do we spec... Our Views?
  101. 101. So, how do we spec... Our Views? We mostly don’t
  102. 102. So, how do we spec... Our Views? We mostly don’t Redundant with “then” steps in stories
  103. 103. So, how do we spec... Our Controllers?
  104. 104. So, how do we spec... Our Controllers? 67% Coverage
  105. 105. So, how do we spec... Our Controllers? 67% Coverage Interaction-based unit test
  106. 106. So, how do we spec... Our Controllers? 67% Coverage Interaction-based unit test Still discussing if it is worth
  107. 107. # tasks_controller_spec.rb describe quot;POST 'create'quot; do before(:each) do @task_attibutes = stub(Hash) @task = stub(Task) @tasks.stub!(:create).and_return(@task) end def do_post post :create, :scheduled_title_id => 1, :task => @task_attributes end it quot;should get the scheduled title from the current companyquot; do @scheduled_titles.should_receive(:find).with(quot;1quot;).and_return(@scheduled_title) do_post end it quot;should create the taskquot; do @tasks.should_receive(:create).with(@task_attributes) do_post end it quot;should redirect to workflowquot; do do_post assigns[:task].should == @task end end
  108. 108. So, how do we spec... Our Models?
  109. 109. So, how do we spec... Our Models? 87% Coverage
  110. 110. So, how do we spec... Our Models? 87% Coverage State-based functional test
  111. 111. So, how do we spec... Our Models? 87% Coverage State-based functional test Only the interesting behavior
  112. 112. describe ScheduledTitle do describe quot;late?quot; do it quot;should be late if begins_at is in the pastquot; do scheduled_title = new_scheduled_title(:begins_at => Date.today - 3.days) scheduled_title.should be_late end it quot;should not be late if begins_at is in the futurequot; do scheduled_title = new_scheduled_title(:begins_at => Date.today + 3.days) scheduled_title.should_not be_late end it quot;should not be late if begins_at is todayquot; do scheduled_title = new_scheduled_title(:begins_at => Date.today) scheduled_title.should_not be_late end end
  113. 113. So, how do we spec... Other objects?
  114. 114. So, how do we spec... Other objects? Helpers -- 32.1% Coverage
  115. 115. So, how do we spec... Other objects? Helpers -- 32.1% Coverage Routes -- never
  116. 116. So, how do we spec... Other objects? Helpers -- 32.1% Coverage Routes -- never Libs -- 86.7%
  117. 117. How do we not spec?
  118. 118. describe User do it { Factory(:user).should have_many(:projects) } it { Factory(:user).should belong_to(:company) } table_has_columns(User, :string, quot;loginquot;) end class User has_many :projects belongs_to :company
  119. 119. describe User do it { Factory(:user).should have_many(:projects) } it { Factory(:user).should belong_to(:company) } table_has_columns(User, :string, quot;loginquot;) end class User create_table quot;usersquot;, :force => true do |t| has_many :projects t.column :login, :string end belongs_to :company
  120. 120. describe User do it { Factory(:user).should have_many(:projects) } it { Factory(:user).should belong_to(:company) } table_has_columns(User, :string, quot;loginquot;) end Structure is not interesting behavior! class User create_table quot;usersquot;, :force => true do |t| has_many :projects t.column :login, :string end belongs_to :company
  121. 121. Progress Ideal Preso ✓ Estimating, Game, Burndown 20 100 ✓ Pair Programming 20 75 ✓ Acceptance vs Unit 20 50 Upfront Design 10 25 Love 10 0 Productivity 20 40’ 35’ 30’ 25’ 20’ 15’ 10’ 5’ End! Total 100 Left: 40
  122. 122. Wednesday
  123. 123. Playboy TV
  124. 124. Progress Ideal Preso ✓ Estimating, Game, Burndown 20 100 ✓ Pair Programming 20 75 ✓ Acceptance vs Unit 20 50 ✓ Upfront Design 10 25 Love 10 0 Productivity 20 40’ 35’ 30’ 25’ 20’ 15’ 10’ 5’ End! Total 100 Left: 30
  125. 125. Thursday
  126. 126. DRY Less code Skinny controller, fat model
  127. 127. Technical debt DRY Less code Skinny controller, fat model
  128. 128. Technical debt Broken windows DRY Less code Skinny controller, fat model
  129. 129. Technical debt Broken windows DRY Less code Skinny controller, fat model Beautiful code
  130. 130. Technical debt Broken windows DRY Less code Skinny controller, fat model Habitable software Beautiful code
  131. 131. Technical debt Broken windows DRY Refactoring towards a deeper insight Less code Skinny controller, fat model Habitable software Beautiful code
  132. 132. Progress Ideal Preso ✓ Estimating, Game, Burndown 20 100 ✓ Pair Programming 20 75 ✓ Acceptance vs Unit 20 50 ✓ Upfront Design 10 25 ✓ Love 10 0 Productivity 20 40’ 35’ 30’ 25’ 20’ 15’ 10’ 5’ End! Total 100 Left: 20
  133. 133. and again, Friday
  134. 134. You call that productivity?
  135. 135. The End

×