SlideShare ist ein Scribd-Unternehmen logo
1 von 27
Downloaden Sie, um offline zu lesen
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
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.
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.
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
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
 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”
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
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
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
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.
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
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
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.
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
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
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.
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.
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
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…
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
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
 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)
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
 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.
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.
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)
www.brightbox.co.uk
hello@brightbox.co.uk
twitter.com/brightbox

Weitere ähnliche Inhalte

Was ist angesagt?

greach 2014 marco vermeulen bdd using cucumber jvm and groovy
greach 2014 marco vermeulen bdd using cucumber jvm and groovygreach 2014 marco vermeulen bdd using cucumber jvm and groovy
greach 2014 marco vermeulen bdd using cucumber jvm and groovyJessie Evangelista
 
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Vysakh Sreenivasan
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperfNew Relic
 
Rspec API Documentation
Rspec API DocumentationRspec API Documentation
Rspec API DocumentationSmartLogic
 
Introduction to testing in Rails
Introduction to testing in RailsIntroduction to testing in Rails
Introduction to testing in Railsbenlcollins
 
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...Ho Chi Minh City Software Testing Club
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
Automated testing with RSpec
Automated testing with RSpecAutomated testing with RSpec
Automated testing with RSpecNascenia IT
 
Intro to Rails Give Camp Atlanta
Intro to Rails Give Camp AtlantaIntro to Rails Give Camp Atlanta
Intro to Rails Give Camp AtlantaJason Noble
 
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Caldera Labs
 
Caldera Learn - LoopConf WP API + Angular FTW Workshop
Caldera Learn - LoopConf WP API + Angular FTW WorkshopCaldera Learn - LoopConf WP API + Angular FTW Workshop
Caldera Learn - LoopConf WP API + Angular FTW WorkshopCalderaLearn
 
Simplifying Code: Monster to Elegant in 5 Steps
Simplifying Code: Monster to Elegant in 5 StepsSimplifying Code: Monster to Elegant in 5 Steps
Simplifying Code: Monster to Elegant in 5 Stepstutec
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?brynary
 

Was ist angesagt? (20)

greach 2014 marco vermeulen bdd using cucumber jvm and groovy
greach 2014 marco vermeulen bdd using cucumber jvm and groovygreach 2014 marco vermeulen bdd using cucumber jvm and groovy
greach 2014 marco vermeulen bdd using cucumber jvm and groovy
 
RSpec 2 Best practices
RSpec 2 Best practicesRSpec 2 Best practices
RSpec 2 Best practices
 
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
 
jQuery Intro
jQuery IntrojQuery Intro
jQuery Intro
 
Rspec API Documentation
Rspec API DocumentationRspec API Documentation
Rspec API Documentation
 
Introduction to testing in Rails
Introduction to testing in RailsIntroduction to testing in Rails
Introduction to testing in Rails
 
RSpec
RSpecRSpec
RSpec
 
I Love codeigniter, You?
I Love codeigniter, You?I Love codeigniter, You?
I Love codeigniter, You?
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
Catalog display
Catalog displayCatalog display
Catalog display
 
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...
[Thong Nguyen & Trong Bui] Behavior Driven Development (BDD) and Automation T...
 
Excellent
ExcellentExcellent
Excellent
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Automated testing with RSpec
Automated testing with RSpecAutomated testing with RSpec
Automated testing with RSpec
 
Intro to Rails Give Camp Atlanta
Intro to Rails Give Camp AtlantaIntro to Rails Give Camp Atlanta
Intro to Rails Give Camp Atlanta
 
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
 
Caldera Learn - LoopConf WP API + Angular FTW Workshop
Caldera Learn - LoopConf WP API + Angular FTW WorkshopCaldera Learn - LoopConf WP API + Angular FTW Workshop
Caldera Learn - LoopConf WP API + Angular FTW Workshop
 
Simplifying Code: Monster to Elegant in 5 Steps
Simplifying Code: Monster to Elegant in 5 StepsSimplifying Code: Monster to Elegant in 5 Steps
Simplifying Code: Monster to Elegant in 5 Steps
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
 

Andere mochten auch

RSpec and Rails
RSpec and RailsRSpec and Rails
RSpec and RailsAlan Hecht
 
RSpec: What, How and Why
RSpec: What, How and WhyRSpec: What, How and Why
RSpec: What, How and WhyRatan Sebastian
 
Introduction to ATDD with Cucumber and RSpec
Introduction to ATDD with Cucumber and RSpecIntroduction to ATDD with Cucumber and RSpec
Introduction to ATDD with Cucumber and RSpecKenta Murata
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails TutorialWen-Tien Chang
 
DevDay 2016: Dave Farley - Acceptance testing for continuous delivery
DevDay 2016: Dave Farley - Acceptance testing for continuous deliveryDevDay 2016: Dave Farley - Acceptance testing for continuous delivery
DevDay 2016: Dave Farley - Acceptance testing for continuous deliveryDevDay Dresden
 
software testing
 software testing software testing
software testingSara shall
 
Rspec and Capybara Intro Tutorial at RailsConf 2013
Rspec and Capybara Intro Tutorial at RailsConf 2013Rspec and Capybara Intro Tutorial at RailsConf 2013
Rspec and Capybara Intro Tutorial at RailsConf 2013Brian Sam-Bodden
 
Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practicesnickokiss
 
Unit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesUnit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesDerek Smith
 
Software Testing Life Cycle
Software Testing Life CycleSoftware Testing Life Cycle
Software Testing Life CycleUdayakumar Sree
 
An Overview of User Acceptance Testing (UAT)
An Overview of User Acceptance Testing (UAT)An Overview of User Acceptance Testing (UAT)
An Overview of User Acceptance Testing (UAT)Usersnap
 
React JS and why it's awesome
React JS and why it's awesomeReact JS and why it's awesome
React JS and why it's awesomeAndrew Hull
 
Software Testing Basics
Software Testing BasicsSoftware Testing Basics
Software Testing BasicsBelal Raslan
 
Introduction to Agile software testing
Introduction to Agile software testingIntroduction to Agile software testing
Introduction to Agile software testingKMS Technology
 
Software Testing Fundamentals
Software Testing FundamentalsSoftware Testing Fundamentals
Software Testing FundamentalsChankey Pathak
 
Software testing basic concepts
Software testing basic conceptsSoftware testing basic concepts
Software testing basic conceptsHưng Hoàng
 

Andere mochten auch (18)

RSpec and Rails
RSpec and RailsRSpec and Rails
RSpec and Rails
 
RSpec: What, How and Why
RSpec: What, How and WhyRSpec: What, How and Why
RSpec: What, How and Why
 
Introduction to ATDD with Cucumber and RSpec
Introduction to ATDD with Cucumber and RSpecIntroduction to ATDD with Cucumber and RSpec
Introduction to ATDD with Cucumber and RSpec
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails Tutorial
 
DevDay 2016: Dave Farley - Acceptance testing for continuous delivery
DevDay 2016: Dave Farley - Acceptance testing for continuous deliveryDevDay 2016: Dave Farley - Acceptance testing for continuous delivery
DevDay 2016: Dave Farley - Acceptance testing for continuous delivery
 
software testing
 software testing software testing
software testing
 
Rspec and Capybara Intro Tutorial at RailsConf 2013
Rspec and Capybara Intro Tutorial at RailsConf 2013Rspec and Capybara Intro Tutorial at RailsConf 2013
Rspec and Capybara Intro Tutorial at RailsConf 2013
 
Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practices
 
Unit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesUnit Testing Concepts and Best Practices
Unit Testing Concepts and Best Practices
 
Software Testing Life Cycle
Software Testing Life CycleSoftware Testing Life Cycle
Software Testing Life Cycle
 
An Overview of User Acceptance Testing (UAT)
An Overview of User Acceptance Testing (UAT)An Overview of User Acceptance Testing (UAT)
An Overview of User Acceptance Testing (UAT)
 
React JS and why it's awesome
React JS and why it's awesomeReact JS and why it's awesome
React JS and why it's awesome
 
Software Testing Basics
Software Testing BasicsSoftware Testing Basics
Software Testing Basics
 
Agilité pour les nuls
Agilité pour les nulsAgilité pour les nuls
Agilité pour les nuls
 
Introduction to Agile software testing
Introduction to Agile software testingIntroduction to Agile software testing
Introduction to Agile software testing
 
Software Testing Fundamentals
Software Testing FundamentalsSoftware Testing Fundamentals
Software Testing Fundamentals
 
Software testing basic concepts
Software testing basic conceptsSoftware testing basic concepts
Software testing basic concepts
 
Software testing ppt
Software testing pptSoftware testing ppt
Software testing ppt
 

Ähnlich wie RSpec User Stories

Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Codescidept
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Testing C# and ASP.net using Ruby
Testing C# and ASP.net using RubyTesting C# and ASP.net using Ruby
Testing C# and ASP.net using RubyBen Hall
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsMike Subelsky
 
Ruby on Rails testing with Rspec
Ruby on Rails testing with RspecRuby on Rails testing with Rspec
Ruby on Rails testing with RspecBunlong Van
 
ruby on rails pitfalls
ruby on rails pitfallsruby on rails pitfalls
ruby on rails pitfallsRobbin Fan
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkDirk Haun
 
User Login in PHP with Session & MySQL.pdf
User Login in PHP with Session & MySQL.pdfUser Login in PHP with Session & MySQL.pdf
User Login in PHP with Session & MySQL.pdfBe Problem Solver
 
Grails Simple Login
Grails Simple LoginGrails Simple Login
Grails Simple Loginmoniguna
 
Plone Interactivity
Plone InteractivityPlone Interactivity
Plone InteractivityEric Steele
 
Ruby onrails cucumber-rspec-capybara
Ruby onrails cucumber-rspec-capybaraRuby onrails cucumber-rspec-capybara
Ruby onrails cucumber-rspec-capybaraBindesh Vijayan
 
Writing Software not Code with Cucumber
Writing Software not Code with CucumberWriting Software not Code with Cucumber
Writing Software not Code with CucumberBen Mabey
 
How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8Katy Slemon
 
Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::ManagerJay Shirley
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4Javier Eguiluz
 
Boston Computing Review - Ruby on Rails
Boston Computing Review - Ruby on RailsBoston Computing Review - Ruby on Rails
Boston Computing Review - Ruby on RailsJohn Brunswick
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-publicChul Ju Hong
 

Ähnlich wie RSpec User Stories (20)

Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Testing C# and ASP.net using Ruby
Testing C# and ASP.net using RubyTesting C# and ASP.net using Ruby
Testing C# and ASP.net using Ruby
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
Ruby on Rails testing with Rspec
Ruby on Rails testing with RspecRuby on Rails testing with Rspec
Ruby on Rails testing with Rspec
 
Well
WellWell
Well
 
ruby on rails pitfalls
ruby on rails pitfallsruby on rails pitfalls
ruby on rails pitfalls
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application Framework
 
Dancing with websocket
Dancing with websocketDancing with websocket
Dancing with websocket
 
User Login in PHP with Session & MySQL.pdf
User Login in PHP with Session & MySQL.pdfUser Login in PHP with Session & MySQL.pdf
User Login in PHP with Session & MySQL.pdf
 
Grails Simple Login
Grails Simple LoginGrails Simple Login
Grails Simple Login
 
Plone Interactivity
Plone InteractivityPlone Interactivity
Plone Interactivity
 
Ruby onrails cucumber-rspec-capybara
Ruby onrails cucumber-rspec-capybaraRuby onrails cucumber-rspec-capybara
Ruby onrails cucumber-rspec-capybara
 
Writing Software not Code with Cucumber
Writing Software not Code with CucumberWriting Software not Code with Cucumber
Writing Software not Code with Cucumber
 
Jsf Ajax
Jsf AjaxJsf Ajax
Jsf Ajax
 
How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8
 
Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::Manager
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Boston Computing Review - Ruby on Rails
Boston Computing Review - Ruby on RailsBoston Computing Review - Ruby on Rails
Boston Computing Review - Ruby on Rails
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
 

Kürzlich hochgeladen

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 

Kürzlich hochgeladen (20)

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 

RSpec User Stories

  • 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)