RSpec User Stories

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

1 comments

Comments 1 - 1 of 1 previous next Post a comment

Post a comment
Embed Video
Edit your comment Cancel

4 Favorites

RSpec User Stories - Presentation 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 \"GET new\" do it \"should show the form to allow someone to register\" do on_getting :new do expect_to_create_a_blank_user end response.should be_success response.should render_template('registrations/new') end describe \"POST create\" do it \"should create and log in a new user\" do on_posting_to :create, :user => { \"some\" => :values } do expect_to_build_a_new_user_with \"some\" => :values expect_to_save_the_new_user_successfully end controller.current_user.should == @user end it \"should redirect to the users dashboard\" do on_posting_to :create, :user => { \"some\" => :values } do expect_to_build_a_new_user_with \"some\" => :values expect_to_save_the_new_user_successfully end
  12. Cont’d… it \"should fail to create a new user if given invalid values\" do on_posting_to :create, :user => { \"some\" => :values } do expect_to_build_a_new_user_with \"some\" => :values expect_to_fail_to_save_the_new_user end controller.current_user.should be_blank end it \"should reshow the registration form if given invalid values\" do on_posting_to :create, :user => { \"some\" => :values } do expect_to_build_a_new_user_with \"some\" => :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 \"some\" => :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 \"should have a username\" do @user = User.new :username => '' @user.should_not be_valid @user.should have(1).errors_on(:username) end it \"should have a unique username\" 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 \"should confirm the password before saving\" do @user = User.new :password => 'secret', :password_confirmation => 'notsecret' @user.should_not be_valid @user.should have(1).errors_on(:password) end it \"should encrypt the password before saving to the database\" 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 \"should have a username\" do @user = a User, :username => '' @user.should_not be_valid @user.should have(1).errors_on(:username) end it \"should have a unique username\" 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 \"should confirm the password before saving\" 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

+ Rahoul BaruahRahoul Baruah, 2 years ago

custom

3848 views, 4 favs, 2 embeds more stats

A step-by-step tutorial outlining the use of RSpec more

More info about this document

© All Rights Reserved

Go to text version

  • Total Views 3848
    • 2121 on SlideShare
    • 1727 from embeds
  • Comments 1
  • Favorites 4
  • Downloads 242
Most viewed embeds
  • 1724 views on http://blog.brightbox.co.uk
  • 3 views on http://static.slideshare.net

more

All embeds
  • 1724 views on http://blog.brightbox.co.uk
  • 3 views on http://static.slideshare.net

less

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

Cancel
File a copyright complaint
Having problems? Go to our helpdesk?

Categories