Your SlideShare is downloading. ×
0
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
RSpec User Stories
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

RSpec User Stories

8,161

Published on

A step-by-step tutorial outlining the use of RSpec and Cucumber to develop applications with automated acceptance tests

A step-by-step tutorial outlining the use of RSpec and Cucumber to develop applications with automated acceptance tests

Published in: Technology
1 Comment
8 Likes
Statistics
Notes
  • By the way - the object-factory now lives at http://github.com/brightbox/object-factory/tree/master
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
8,161
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
393
Comments
1
Likes
8
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. RSpec and User Stories A step by step tutorial By Rahoul Baruah http://www.brightbox.co.uk Released under the Creative Commons Attribution Share-Alike Licence
  • 2. What is RSpec?  Behaviour Driven Development  An evolution of Test Driven Development  Concentrates on the “behaviour” of your system  Specify the behaviour first, implement it second, refactor it third.
  • 3. What are User Stories?  Acceptance Tests for RSpec  Describe the functionality of your application in terms your customer will understand  Prove that the application does what it is supposed to.
  • 4. A Simple Authentication System  Write the feature  Write verification code for the feature  Specify the controller  Implement the controller  Specify the models  Implement the models  Verify the feature works as required  Refactor
  • 5. Write our Feature  Write our feature and save it as features/ allow-a-user-to-login.feature  Show it to the customer and have it approved  Run rake features
  • 6.  Feature: Allow a User to log in  As a user,  I want to log in,  So I can see my stuff  Scenario: Successful login  Given a user called Dave with a password of secret  And 15 items of stuff  When I log in as Dave with password secret  Then I should see the Dashboard page  And it should list my 15 items of stuff  Scenario: Unsuccessful login  Given a user called Dave with a password of secret  When I log in as Dave with password potato  Then I should see the Login page  And I should see a message saying “an incorrect username or password was supplied”
  • 7. rake features  When we run ‘rake features’ it tells us that none of the features are implemented  So we start with the first step and implement that
  • 8. Steps - proving a feature works  Create Ruby files, registration-steps.rb and session-steps.rb, in features/steps  These contain the code verifying that the feature works as expected
  • 9. registration and session steps Given /^a user called (.*) with a password of (.*)$/ do | username, password | user = User.find_by_username username user.destroy unless user.nil? visits '/registrations/new' fills_in 'User name', :with => username fills_in 'Password', :with => password fills_in 'Password Confirmation', :with => password clicks_button 'Register' end When /^I log in as (.*) with password (.*)$/ do | username, password | visits '/sessions/new' fills_in 'User name', :with => username fills_in 'Password', :with => password clicks_button 'Log in' end Then /^I should see the Dashboard page$/ do response.should redirect_to('/dashboard') end  Use Webrat to define our interactions with the application  Use RSpec to check the responses from the application
  • 10. Specify our Controller  ‘rake features’ fails  So we need to start writing some code to make it pass  But before we write any code, we need a specification  So we run… ruby script/generate rspec_controller Registrations …to build a blank controller and specification  Now to implement the RegistrationsController.  Similar work needs to be done with the SessionsController for actually logging in.
  • 11. describe RegistrationsController do describe quot;GET newquot; do it quot;should show the form to allow someone to registerquot; do on_getting :new do expect_to_create_a_blank_user end response.should be_success response.should render_template('registrations/new') end describe quot;POST createquot; do it quot;should create and log in a new userquot; do on_posting_to :create, :user => { quot;somequot; => :values } do expect_to_build_a_new_user_with quot;somequot; => :values expect_to_save_the_new_user_successfully end controller.current_user.should == @user end it quot;should redirect to the users dashboardquot; do on_posting_to :create, :user => { quot;somequot; => :values } do expect_to_build_a_new_user_with quot;somequot; => :values expect_to_save_the_new_user_successfully end
  • 12. Cont’d… it quot;should fail to create a new user if given invalid valuesquot; do on_posting_to :create, :user => { quot;somequot; => :values } do expect_to_build_a_new_user_with quot;somequot; => :values expect_to_fail_to_save_the_new_user end controller.current_user.should be_blank end it quot;should reshow the registration form if given invalid valuesquot; do on_posting_to :create, :user => { quot;somequot; => :values } do expect_to_build_a_new_user_with quot;somequot; => :values expect_to_fail_to_save_the_new_user end response.should render_template('/sessions/new') flash[:error].should == 'There were some errors when registering your details' end end end
  • 13. Specification for new registrations  Use helper methods to make the specification easier to read  Use mock objects so we are testing just the controller, not the (non-existent) models  Note how we can supply quot;somequot; => :values for the :registration parameter. As we are not using real models, the actual fields do not matter; what counts is how the controller behaves in response to certain actions.
  • 14. def expect_to_create_a_blank_user @user = mock_model User User.should_receive(:new).and_return(@user) end def expect_to_build_a_new_user_with parameters @user = mock_model User User.should_receive(:new).with(parameters).and_return(@user) end def expect_to_save_the_new_user_successfully @user.should_receive(:save!).and_return(true) end def expect_to_fail_to_save_the_new_user prepare_for_errors_on @user @user.should_receive(:save!).and_raise(ActiveRecord::RecordInvalid.new(@user)) end  The helpers build a mock user  We tell the mock to expect certain method calls and return the correct responses
  • 15. Implement the Controller  We do need to build a model… ruby script/generate rspec_model User …so that our steps will run  Remove the < ActiveRecord::Base from the definition for now (so that ActiveRecord does not complain about our lack of database tables)  But we don’t implement anything in it yet; our controller specification is using mocks so does not actually need a real object  We also need to add some routes to get things moving… map.resources :registrations map.resources :sessions map.resource :dashboard
  • 16. Implementing the Controller class RegistrationsController < ApplicationController def new @user = User.new end def create @user = User.new params[:user] @user.save! redirect_to dashboard_path rescue ActiveRecord::RecordInvalid flash[:error] = 'There were some errors when registering your details' render :template => 'registrations/new', :status => 422 end end  The implementation is pretty simple, which is exactly as it should be.
  • 17. Specifying the Model  Now we have our controller behaving as specified we need to specify the behaviour of our User model  We have already generated the RSpec files, we just need to tell it how we expect a User to behave.
  • 18. Specifying the Model  We write the specs  Update the migration to deal with the fields we need  We switch the model back so that it descends from ActiveRecord::Base  We run the migration  We implement the model
  • 19. Specifying the Model First attempt… describe User do it quot;should have a usernamequot; do @user = User.new :username => '' @user.should_not be_valid @user.should have(1).errors_on(:username) end it quot;should have a unique usernamequot; do @first_user = User.create! :username => 'arthur', :password => '12345', :password_confirmation => '12345' @second_user = User.new :username => 'arthur' @second_user.should_not be_valid @second_user.should have(1).errors.on(:username) end Cont’d…
  • 20. Cont’d… it quot;should confirm the password before savingquot; do @user = User.new :password => 'secret', :password_confirmation => 'notsecret' @user.should_not be_valid @user.should have(1).errors_on(:password) end it quot;should encrypt the password before saving to the databasequot; do PasswordEncrypter.should_receive(:encrypt).with('12345').and_return('3ncrypt3d') @user = User.new :username => 'arthur', :password => '12345', :password_confirmation => '12345' @user.save! @user.encrypted_password.should == '3ncrypt3d' end end
  • 21. Specifying the Model  There are two areas of the specification that “smell bad”  Both “it should have a unique username” and “it should encrypt the password before saving to the database” require a valid object to be saved; which in turn require knowledge of the object beyond that individual specification clause  Ideally this would not be necessary but is a problem with the ActiveRecord pattern; mixing business logic and persistence logic in a single class
  • 22.  The (imperfect) solution is to use object ‘factories’ that encapsulate knowledge of a valid model.  We are still spreading that knowledge outside of the specification but at least it is a single place to make that change (usually in spec/ spec_helper.rb)
  • 23. describe User do it quot;should have a usernamequot; do @user = a User, :username => '' @user.should_not be_valid @user.should have(1).errors_on(:username) end it quot;should have a unique usernamequot; do @first_user = a_saved User, :username => ‘arthur’ @second_user = a User, :username => 'arthur' @second_user.should_not be_valid @second_user.should have(1).errors.on(:username) end it quot;should confirm the password before savingquot; do @user = a User, :password => 'secret', :password_confirmation => 'notsecret' @user.should_not be_valid @user.should have(1).errors_on(:password) end
  • 24.  Rewritten to use “a Something” and “a_saved Something” as an object factory  Each specification clause only specifies the information it needs.  The factory ensures that the rest of the object is valid.
  • 25. Acceptance  By now we should have done enough to let the first step in our story pass its acceptance test  rake features will prove it  That should be all we need to do for that particular step - any extra development is unnecessary as it has not been requested by the customer  However, we can now safely refactor the code, to organise it better, safe in the knowledge that we can prove that it still does what is required without breakages.
  • 26. Appendix  We have a fork of RSpec for Rails that provides a set of helper methods: prepare_for_errors_on, on_getting, on_posting_to, on_putting_to and on_deleting_from  See http://github.com/rahoulb/rspec-rails/wikis/home  We are working on an object factory that makes building models (without full knowledge of their internals) a bit simpler… Object.factory.when_creating_a User, :auto_generate => :username, :auto_confirm => :password @user = a User @user = a_saved User # as above but auto-saves the user @user.should be_valid  See http://github.com/rahoulb/object-factory/wikis (although at the time of writing, November 2008, this is not quite ready)
  • 27. www.brightbox.co.uk hello@brightbox.co.uk twitter.com/brightbox

×