SlideShare une entreprise Scribd logo
1  sur  86
Télécharger pour lire hors ligne
TDD with Rails 3

 Wolfram Arnold
 @wolframarnold
www.rubyfocus.biz

In collaboration with:
LA Ruby Conference
Introduction
What?
What?

●   Why TDD?
●   Rails 3 & TDD
       –   what's changed?
       –   RSpec 2
●   Testing in Layers
●   TDD'ing model development
●   Factories, mocks, stubs...
●   Controllers & Views
How?
How?

●   Presentation
●   Live coding demos
●   In-class exercises
       –   Pair programming
●   Material from current development practice
●   Fun
It works best, when...

Active participation
Try something new
Team Effort
  Pairing
Efficient Rails
 Test-Driven
Development
Why “efficient” and “testing”?

“Testing takes too much time.”
“It's more efficient to test later.”
“Testing is the responsibility of QA, not
  developers.”
“It's not practical to test X.”
“Tests keep breaking too often.”
   When data changes.
   When UI design changes.
The Role of Testing

Development without tests...
  fails to empower developers to efficiently take
    responsibility for quality of the code delivered
  makes collaboration harder
  build narrow silos of expertise
  instills fear & resistance to change
  makes documentation a chore
  stops being efficient very soon
TDD: Keeping cost of change low
Cost per change
                                  without
                                   TDD




                                    with
                                    TDD




                                     Time
Why?

Non-TDD
 Accumulates “technical debt” unchecked
 Removal of technical debt carries risk
    The more technical debt, the higher the risk
 Existing technical debt attracts more technical debt
    Like compound interest
    People are most likely to do what others did before them
    To break the pattern heroic discipline & coordination
      required
Testing in Layers

 Application, Browser UI         Selenium 1, 2


                           RSpec Request, Capybara    Test::Unit Integration
   Application, Server
                              Cucumber, Webrat

                            RSpec           RSpec
 Views           Helpers
                            Views           Helpers

                            RSpec           RSpec     Test::Unit Functional
Controller       Routes
                           Controller       Routes

                                    RSpec             Test::Unit
         Model
                                    Model
Cost of Testing
                           Relationship to data


                           most
 Application, Browser UI   removed


   Application, Server



 Views           Helpers



Controller       Routes



         Model             closest



                                                  Cost
Best ROI for Testing
                           Layers


 Application, Browser UI



   Application, Server



 Views           Helpers



Controller       Routes



         Model



                                    Impact/Line of Test Code
TDD & Design Patterns

Skinny Controller—   ➢   Designed to move logic
 Fat Model               from higher to lower
DRY                      application layers
Scopes               ➢   Following design
                         patterns makes testing
Proxy Associations       easier
Validations          ➢   Code written following
...                      TDD economics will
                         naturally converge on
                         these design patterns!
Rails 3 – what's new?
●   gem management with bundler
●   scripts: rails g, s, ...
●   constants: RAILS_ENV → Rails.env...
●   errors.on(:key) → errors[:key], always Array now
●   routes: match '/' => 'welcome#index'
●   configuration in application.rb
●   ActiveRecord: Scopes, Relations, Validations
●   Controllers: no more verify
●   ActionMailer: API overhaul
●   Views: auto-escaped, unobtrusive JS
RSpec 2

●   Filters to run select tests
       –   RSpec.configure do |c|
             c.filter_run :focus => true
            end
●   Model specs:
       –   be_a_new(Array)
●   Controller specs:
       –   integrate_views → render_views
       –   assigns[:key]=val → assigns(:key,val)
             (deprecated)
RSpec 2 cont'd

●   View specs:
       –   response → rendered
       –   assigns[:key]=val → assign(:key, val)       (Req)
●   Routing specs:
       –   route_for is gone
       –   route_to, be_routable (also in Rspec 1.3)
Know Your

Tools
RVM

●   multiple, isolated Rubies
●   can have different gemsets each

    Install: http://rvm.beginrescueend.com/rvm/install/
    As User or System-Wide


    > rvm install ruby-1.8.7
    > rvm gemset create rails3
    > rvm ruby-1.8.7@rails3
    > rvm info
RVM Settings

●   System: /etc/rvmrc
●   User:        ~/.rvmrc
●   Project: .rvmrc in project(s) root

    > mkdir workspace
    > cd workspace
    > echo “ruby-1.8.7@rails3” > .rvmrc
    > cd ../workspace
    > rvm info
    > gem list
Installing gems

●   Do NOT use sudo with RVM!!!
●   gems are specific to the Ruby and the gemset



    > rvm info   → make sure we're on gemset “rails3”
    > gem install rails
    > gem install rspec-rails
    > gem list
Rails 3: rails command

●   Replaces script/*
       –   new
       –   console
       –   dbconsole
       –   generate
       –   server
Let's do some coding

Demo
> rails generate rspec:install


> rails generate model User first_name:string
  last_name:string email:string
TDD Cycle

●   Start user story
●   Experiment
●   Write test
●   Write code
●   Refactor
●   Finish user story
Structure of Tests

Setup
Expected value
Actual value
Verification: actual == expected?
Teardown
Good Tests are...

Compact
Responsible for testing one concern only
Fast
DRY
RSpec Verifications

should respond_to
should be_nil
   → works with any ? method (so-called “predicates”)
should be_valid
should_not be_nil; should_not be_valid
lambda {...}.should change(), {}, .from().to(), .by()
should ==, equal, eq, be
RSpec Structure

before, before(:each), before(:all)
after, after(:each), after(:all)
describe do...end, nested
it do... end
RSpec Subject
describe Address do
      it “must have a street” do
        a = Address.new
        a.should_not be_valid
        a.errors.on(:street).should_not be_nil
      end
      #subject { Address.new } # Can be omitted if .new
                               # on same class as in describe
      it “must have a street” do
        should_not be_valid # should is called on
                             # subject by default
        subject.errors.on(:street).should_not be_nil
      end


end
RSpec2

●   https://github.com/rspec/rspec-rails
●   http://blog.davidchelimsky.net/
●   http://relishapp.com/rspec
●   More modular, some API changes
    Gemspec file, for Rails 3:


    group :development, :test do
      gem 'rspec-rails', "~> 2.0.1"
    end
Models: What to test?

Validation Rules
Associations
Any custom method
Association Proxy Methods
Let's do some coding

Exercise...
Story Exercise #1

A User object must have a first and last name.
A User object can construct a full name from the
  first and last name.
A User object has an optional middle name.
A User object returns a full name including, if
  present, the middle name.
RSpec ==, eql, equal

obj.should == 5            5 == 5
obj.should eq(5)
obj.should equal(5)        5.equal 5
obj.should be(5)
                                    Use == or eq
Object Equality vs. Identity
                                    Unless you know you
eql, ==     compare values          need something else

equal, === compare objects,
           classes
        Warning! Do not use != with RSpec.
        Use should_not instead.
RSpec should change

lambda {…}.should change...
expect {…}.to change...

expect {
   Person.create
}.to change(Person, :count).from(0).to(1)


lambda {
  @bob.addresses.create(:street => “...”)
}.should change{@bob.addresses.count}.by(1)
Models
What to test?
Test Models for...

●   validation
●   side-effects before/after saving
●   associations
●   association proxy methods
●   scopes, custom finders
●   nested attributes
●   observers
●   custom methods
valid?
How to Test for Validations?

it 'requires X' do
    n = Model.new
    n.should_not be_valid
    n.errors[:x].should_not be_empty
end


●   Instantiate object with invalid property
●   Check for not valid?
●   Check for error on right attribute
Check for Side Effects
Model Callbacks

Requirement:                Callbacks:
  Default a value before      before_save
   saving                     after_save
  Send an email after         after_destroy
   saving
                              ...
  Post to a URL on delete
  ...
How to test Callbacks?
Through their Side Effects:
●   Set up object in state before callback
●   Trigger callback
●   Check for side effect

it 'encrypts password on save' do
    n = User.new
    n.should_not be_valid
    n.errors.on(:x).should_not be_nil
end
How are Callbacks triggered?

Callback                        Trigger event
  before_validation               valid?
  after_validation                valid?
  before_save                     save, create
  after_save                      save, create
  before_create                   create
  after_create                    create
  before_destroy                  destroy
  after_destroy                   destroy
  after_find (see docs)           find
  after_initialize (see docs)     new
Associations
Model Associations

Requirement:                has_many
  Entities have             has_one
   relationships
                            belongs_to
  Given an object, I want
   to find all related
   objects
                            has_many :through
Tables and Associations




class Customer < AR::Base                       class Order < AR::Base
      has_many :orders                                belongs_to :customer
      ...                                             ...
end                                             end

Source: Rails Guides, http://guides.rubyonrails.org/association_basics.html
Migrations and Associations

create_table :addresses do |t|   class Address < AR::Base
      t.belongs_to :person             belongs_to :person
      # same as:                       ...
      # t.integer :person_id
                                 end
      ...
end

                                 class Person < AR::Base
create_table :people do |t|            has_many :addresses
    ...
end                                    ...
                                 end
Association Methods

belongs_to :person                             has_many :assets
     .person                                       .assets
     .person =                                     .assets <<
     .build_person()                               .assets = [...]
     .create_person()                              .assets.delete(obj,..)
                                                   .assets.clear
                                                   .assets.empty?
                                                   .assets.create(...)
                                                   .assets.build(...)
                                                   .assets.find(...)



Source: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
has_many
:through

many-to-many
relationships
Indices for Associations

Rule: Any database column that can occur in a
 WHERE clause should have an index

    create_table :addresses do |t|
       t.belongs_to :person
      # same as:
      # t.integer :person_id
      ...
    end
    add_index :addresses, :person_id
How to test for Associations?

●   Are the association methods present?
●   Checking for one is enough.
●   No need to “test Rails” unless using
    associations with options
●   Check that method runs, if options used
it “has many addresses” do
    p = Person.new
    p.should respond_to(:addresses)
end
Association Options

Ordering
  has_many :people, :order => “last_name ASC”
Class Name
  belongs_to :customer, :class_name => “Person”
Foreign Key
  has_many :messages, :foreign_key => “recipient_id”
Conditions
  has_many :unread_messages,
   :class_name => “Message”,
   :conditions => {:read_at => nil}
How to test Assn's with Options?

●   Set up a non-trivial data set.
●   Verify that it's non-trival.
●   Run association method having options
●   Verify result
it “sorts addresses by zip” do
    p = Factory(:person)
    # Factory for addrs with zip 23456, 12345
    Address.all.should == [addr1, addr2]
    p.addresses.should == [addr2, addr1]
    p.should respond_to(:addresses)
end
More Association Options

Joins
  has_many :popular_items,
   :class_name => “Item”,
   :include => :orders,
   :group => “orders.customer_id”,
   :order => “count(orders.customer_id) DESC”
Exercise

A User can have 0 or more Addresses.
A User's Address must have a street, city, state and zip.
A User's Address can have an optional 2-letter country
  code.
If the country is left blank, it should default to “US” prior
   to saving.
Extra Credit:
State is required only if country is “US” or “CA”
Zip must be numerical if country is “US”
Controllers
Controllers

Controllers are pass-through entities
Mostly boilerplate—biz logic belongs in the model
Controllers are “dumb” or “skinny”
They follow a run-of-the mill pattern:


              the Controller Formula
Controller RESTful Actions

Display methods (“Read”)
  GET: index, show, new, edit
Update method
  PUT
Create method
  POST
Delete method
  DELETE
REST?

Representational State Transfer


All resource-based applications & API's need to
  do similar things, namely:
create, read, update, delete


It's a convention:
  no configuration, no ceremony
  superior to CORBA, SOAP, etc.
RESTful rsources in Rails

map.resources :people (in config/routes.rb)
      people_path, people_url        “named route methods”
      GET     /people          → “index” action
      POST /people             → “create” action
      new_person_path, new_person_url
      GET     /people/new      → “new” action
      edit_person_path, edit_person_url
      GET     /people/:id/edit → “edit” action with ID
      person_path, person_url
      GET     /people/:id      → “show” action with ID
      PUT     /people/:id      → “update” action with ID
      DELETE /people/:id       → “destroy” action with ID
Read Formula

Find data, based on parameters
Assign variables
Render
Reads Test Pattern

Make request (with id of record if a single record)
Check Rendering
  correct template
  redirect
  status code
  content type (HTML, JSON, XML,...)
Verify Variable Assignments
  required by view
Create/Update Formula

Update: Find record from parameters
Create: Instantiate new model object
Assign form fields parameters to model object
  This should be a single line
  It is a pattern, the “Controller Formula”
Save
Handle success—typically a redirect
Handle failure—typically a render
Create/Update Test Pattern

Make request with form fields to be created/upd'd
Verify Variable Assignments
Verify Check Success
  Rendering
Verify Failure/Error Case
  Rendering
  Variables
Verify HTTP Verb protection
How much test is too much?

Test anything where the code deviates from
  defaults, e.g. redirect vs. straight up render
These tests are not strictly necessary:
  response.should be_success
  response.should render_template('new')
Test anything required for the application to
  proceed without error
  Speficially variable assignments
Do test error handling code!
How much is enough?

Notice: No view testing so far.
Emphasize behavior over display.
Check that the application handles errors
 correctly
Test views only for things that could go wrong
  badly
  incorrect form URL
  incorrect names on complicated forms, because they
    impact parameter representation
View Testing

RSpec controllers do not render views (by
 default)
Test form urls, any logic and input names
Understand CSS selector syntax
View test requires set up of variables
  another reason why there should only be very few
   variables between controller and view
  some mocks here are OK
RSpec 2 View Update

●   should have_tag is gone
●   Use webrat matchers:
       –   Add “webrat” to Gemfile
       –   Add require 'webrat/core/matchers' to
            spec_helper.rb
       –   matcher is should have_selector(“css3”)
●   response is now rendered
●   rendered.should have_selector(“css3”)
Mocks,
Doubles,
Stubs, ...
Object level

All three create a “mock”
  object.


mock(), stub(), double() at
                              m = mock(“A Mock”)
 the Object level are
 synonymous                   m = stub(“A Mock”)
                              m = double(“A Mock”)

Name for error reporting
Using Mocks

Mocks can have method      m = mock(“A Mock”)
 stubs.                    m.stub(:foo)
They can be called like    m.foo => nil
  methods.
Method stubs can return    m.stub(:foo).
 values.                     and_return(“hello”)
                           m.foo => “hello”
Mocks can be set up with
 built-in method stubs.
                           m = mock(“A Mock”,
                               :foo => “hello”)
Message Expectations

Mocks can carry message m = mock(“A Mock”)
 expectations.
should_receive expects a m.should_receive(:foo)
  single call by default
Message expectations can m.should_receive(:foo).
 return values.            and_return(“hello”)


Can expect multiple calls. m.should_receive(:foo).
                          twice
                        m.should_receive(:foo).
                          exactly(5).times
Argument Expectations

                      m = mock(“A Mock”)
Regular expressions   m.should_receive(:foo).
                        with(/ello/)

Hash keys
                      with(hash_including(
                        :name => 'joe'))


Block                 with { |arg1, arg2|
                        arg1.should == 'abc'
                        arg2.should == 2
                      }
Partial Mocks

                         jan1 =
                           Time.civil(2010)

Replace a method on an
 existing class.         Time.stub!(:now).
                           and_return(jan1)

Add a method to an
                         Time.stub!(:jan1).
 existing class.
                           and_return(jan1)
Dangers
  of
 Mocks
Problems

Non-DRY
  Simulated API vs. actual API
Maintenance
  Simulated API gets out of sync with actual API
  Tedious to remove after “outside-in” phase
Leads to testing implementation, not effect
Demands on integration and exploratory testing
 higher with mocks.
Less value per line of test code!
So what are they good for?

External services
  API's
System services
  Time
  I/O, Files, ...
Sufficiently mature (!) internal API's
  Slow queries
  Queries with complicated data setup
TDD with

Webservices
    Amazon RSS Feed
     SimpleRSS gem
 Nokogiri XML parser gem
     FakeWeb mocks
Step 1: Experiment
Step 2:
Proof of Concept
Step 3:
Specs & Refactor
Exercise: Step 3

●   Using TDD techniques with
       –   FakeWeb
       –   mocks
●   Build up a Product model with:
       –   a fetch class method returning an array of
             Product instances
       –   instance methods for:
               ●   title, description, link
               ●   image_url (extracted from description)
●   Refactor controller & view to use Product
     model
Reference

●   https://github.com/wolframarnold/Efficient-
      TDD-Rails3
●   Class Videos: http://goo.gl/Pe6jE
●   Rspec Book
●   https://github.com/rspec/rspec-rails
●   http://blog.davidchelimsky.net/
●   http://relishapp.com/rspec

Contenu connexe

Tendances

Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to SwaggerKnoldus Inc.
 
Modern Functional Fluent ColdFusion REST Apis
Modern Functional Fluent ColdFusion REST ApisModern Functional Fluent ColdFusion REST Apis
Modern Functional Fluent ColdFusion REST ApisOrtus Solutions, Corp
 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingRoman Liubun
 
Karate for Complex Web-Service API Testing by Peter Thomas
Karate for Complex Web-Service API Testing by Peter ThomasKarate for Complex Web-Service API Testing by Peter Thomas
Karate for Complex Web-Service API Testing by Peter Thomasintuit_india
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsRan Mizrahi
 
Deploying a Location-Aware Ember Application
Deploying a Location-Aware Ember ApplicationDeploying a Location-Aware Ember Application
Deploying a Location-Aware Ember ApplicationBen Limmer
 
django Forms in a Web API World
django Forms in a Web API Worlddjango Forms in a Web API World
django Forms in a Web API WorldTareque Hossain
 
Dynamic Application Development by NodeJS ,AngularJS with OrientDB
Dynamic Application Development by NodeJS ,AngularJS with OrientDBDynamic Application Development by NodeJS ,AngularJS with OrientDB
Dynamic Application Development by NodeJS ,AngularJS with OrientDBApaichon Punopas
 
Behavior Driven Development with Cucumber
Behavior Driven Development with CucumberBehavior Driven Development with Cucumber
Behavior Driven Development with CucumberBrandon Keepers
 
Karate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleKarate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleVodqaBLR
 
Karate - MoT Dallas 26-Oct-2017
Karate - MoT Dallas 26-Oct-2017Karate - MoT Dallas 26-Oct-2017
Karate - MoT Dallas 26-Oct-2017Peter Thomas
 
Automate right start from API
Automate right start from APIAutomate right start from API
Automate right start from APIRoman Liubun
 
Introduction to testing in Rails
Introduction to testing in RailsIntroduction to testing in Rails
Introduction to testing in Railsbenlcollins
 
Building an App with jQuery and JAXER
Building an App with jQuery and JAXERBuilding an App with jQuery and JAXER
Building an App with jQuery and JAXERBrian Moschel
 
Introduce cucumber
Introduce cucumberIntroduce cucumber
Introduce cucumberBachue Zhou
 
Coffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSCoffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSDeepu S Nath
 
Put a Button on It: Removing Barriers to Going Fast
Put a Button on It: Removing Barriers to Going FastPut a Button on It: Removing Barriers to Going Fast
Put a Button on It: Removing Barriers to Going FastOSCON Byrum
 
CUCUMBER - Making BDD Fun
CUCUMBER - Making BDD FunCUCUMBER - Making BDD Fun
CUCUMBER - Making BDD FunSQABD
 

Tendances (20)

Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to Swagger
 
Gemboys
GemboysGemboys
Gemboys
 
The CQRS diet
The CQRS dietThe CQRS diet
The CQRS diet
 
Modern Functional Fluent ColdFusion REST Apis
Modern Functional Fluent ColdFusion REST ApisModern Functional Fluent ColdFusion REST Apis
Modern Functional Fluent ColdFusion REST Apis
 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testing
 
Karate for Complex Web-Service API Testing by Peter Thomas
Karate for Complex Web-Service API Testing by Peter ThomasKarate for Complex Web-Service API Testing by Peter Thomas
Karate for Complex Web-Service API Testing by Peter Thomas
 
How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design Patterns
 
Deploying a Location-Aware Ember Application
Deploying a Location-Aware Ember ApplicationDeploying a Location-Aware Ember Application
Deploying a Location-Aware Ember Application
 
django Forms in a Web API World
django Forms in a Web API Worlddjango Forms in a Web API World
django Forms in a Web API World
 
Dynamic Application Development by NodeJS ,AngularJS with OrientDB
Dynamic Application Development by NodeJS ,AngularJS with OrientDBDynamic Application Development by NodeJS ,AngularJS with OrientDB
Dynamic Application Development by NodeJS ,AngularJS with OrientDB
 
Behavior Driven Development with Cucumber
Behavior Driven Development with CucumberBehavior Driven Development with Cucumber
Behavior Driven Development with Cucumber
 
Karate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made SimpleKarate - Web-Service API Testing Made Simple
Karate - Web-Service API Testing Made Simple
 
Karate - MoT Dallas 26-Oct-2017
Karate - MoT Dallas 26-Oct-2017Karate - MoT Dallas 26-Oct-2017
Karate - MoT Dallas 26-Oct-2017
 
Automate right start from API
Automate right start from APIAutomate right start from API
Automate right start from API
 
Introduction to testing in Rails
Introduction to testing in RailsIntroduction to testing in Rails
Introduction to testing in Rails
 
Building an App with jQuery and JAXER
Building an App with jQuery and JAXERBuilding an App with jQuery and JAXER
Building an App with jQuery and JAXER
 
Introduce cucumber
Introduce cucumberIntroduce cucumber
Introduce cucumber
 
Coffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSCoffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JS
 
Put a Button on It: Removing Barriers to Going Fast
Put a Button on It: Removing Barriers to Going FastPut a Button on It: Removing Barriers to Going Fast
Put a Button on It: Removing Barriers to Going Fast
 
CUCUMBER - Making BDD Fun
CUCUMBER - Making BDD FunCUCUMBER - Making BDD Fun
CUCUMBER - Making BDD Fun
 

Similaire à 2011-02-03 LA RubyConf Rails3 TDD Workshop

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
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular IntermediateLinkMe Srl
 
Ruby on Rails: Coding Guideline
Ruby on Rails: Coding GuidelineRuby on Rails: Coding Guideline
Ruby on Rails: Coding GuidelineNascenia IT
 
Efficient Rails Test Driven Development (class 4) by Wolfram Arnold
Efficient Rails Test Driven Development (class 4) by Wolfram ArnoldEfficient Rails Test Driven Development (class 4) by Wolfram Arnold
Efficient Rails Test Driven Development (class 4) by Wolfram ArnoldMarakana Inc.
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiRan Mizrahi
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on RailsMark Menard
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteDr Nic Williams
 
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram ArnoldEfficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram ArnoldMarakana Inc.
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails AppsRabble .
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your CodeNate Abele
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0Nate Abele
 
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...Nilesh Panchal
 
The "Holy Grail" of Dev/Ops
The "Holy Grail" of Dev/OpsThe "Holy Grail" of Dev/Ops
The "Holy Grail" of Dev/OpsErik Osterman
 

Similaire à 2011-02-03 LA RubyConf Rails3 TDD Workshop (20)

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
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Ruby On Rails
Ruby On RailsRuby On Rails
Ruby On Rails
 
Ruby on Rails: Coding Guideline
Ruby on Rails: Coding GuidelineRuby on Rails: Coding Guideline
Ruby on Rails: Coding Guideline
 
Efficient Rails Test Driven Development (class 4) by Wolfram Arnold
Efficient Rails Test Driven Development (class 4) by Wolfram ArnoldEfficient Rails Test Driven Development (class 4) by Wolfram Arnold
Efficient Rails Test Driven Development (class 4) by Wolfram Arnold
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Rspec
RspecRspec
Rspec
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Ruby On Rails
Ruby On RailsRuby On Rails
Ruby On Rails
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on Rails
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
 
Rspec
RspecRspec
Rspec
 
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram ArnoldEfficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your Code
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0
 
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...
Ruby on-rails-101-presentation-slides-for-a-five-day-introductory-course-1194...
 
The "Holy Grail" of Dev/Ops
The "Holy Grail" of Dev/OpsThe "Holy Grail" of Dev/Ops
The "Holy Grail" of Dev/Ops
 

Dernier

Building AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxBuilding AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxUdaiappa Ramachandran
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?IES VE
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationIES VE
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsSeth Reyes
 
Linked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesLinked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesDavid Newbury
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsSafe Software
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...Aggregage
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Websitedgelyza
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7DianaGray10
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.YounusS2
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1DianaGray10
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding TeamAdam Moalla
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024SkyPlanner
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostMatt Ray
 

Dernier (20)

Building AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptxBuilding AI-Driven Apps Using Semantic Kernel.pptx
Building AI-Driven Apps Using Semantic Kernel.pptx
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
20230104 - machine vision
20230104 - machine vision20230104 - machine vision
20230104 - machine vision
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and Hazards
 
Linked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesLinked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond Ontologies
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Website
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
 

2011-02-03 LA RubyConf Rails3 TDD Workshop

  • 1. TDD with Rails 3 Wolfram Arnold @wolframarnold www.rubyfocus.biz In collaboration with: LA Ruby Conference
  • 4. What? ● Why TDD? ● Rails 3 & TDD – what's changed? – RSpec 2 ● Testing in Layers ● TDD'ing model development ● Factories, mocks, stubs... ● Controllers & Views
  • 6. How? ● Presentation ● Live coding demos ● In-class exercises – Pair programming ● Material from current development practice ● Fun
  • 7. It works best, when... Active participation Try something new Team Effort Pairing
  • 9. Why “efficient” and “testing”? “Testing takes too much time.” “It's more efficient to test later.” “Testing is the responsibility of QA, not developers.” “It's not practical to test X.” “Tests keep breaking too often.” When data changes. When UI design changes.
  • 10. The Role of Testing Development without tests... fails to empower developers to efficiently take responsibility for quality of the code delivered makes collaboration harder build narrow silos of expertise instills fear & resistance to change makes documentation a chore stops being efficient very soon
  • 11. TDD: Keeping cost of change low Cost per change without TDD with TDD Time
  • 12. Why? Non-TDD Accumulates “technical debt” unchecked Removal of technical debt carries risk The more technical debt, the higher the risk Existing technical debt attracts more technical debt Like compound interest People are most likely to do what others did before them To break the pattern heroic discipline & coordination required
  • 13. Testing in Layers Application, Browser UI Selenium 1, 2 RSpec Request, Capybara Test::Unit Integration Application, Server Cucumber, Webrat RSpec RSpec Views Helpers Views Helpers RSpec RSpec Test::Unit Functional Controller Routes Controller Routes RSpec Test::Unit Model Model
  • 14. Cost of Testing Relationship to data most Application, Browser UI removed Application, Server Views Helpers Controller Routes Model closest Cost
  • 15. Best ROI for Testing Layers Application, Browser UI Application, Server Views Helpers Controller Routes Model Impact/Line of Test Code
  • 16. TDD & Design Patterns Skinny Controller— ➢ Designed to move logic Fat Model from higher to lower DRY application layers Scopes ➢ Following design patterns makes testing Proxy Associations easier Validations ➢ Code written following ... TDD economics will naturally converge on these design patterns!
  • 17. Rails 3 – what's new? ● gem management with bundler ● scripts: rails g, s, ... ● constants: RAILS_ENV → Rails.env... ● errors.on(:key) → errors[:key], always Array now ● routes: match '/' => 'welcome#index' ● configuration in application.rb ● ActiveRecord: Scopes, Relations, Validations ● Controllers: no more verify ● ActionMailer: API overhaul ● Views: auto-escaped, unobtrusive JS
  • 18. RSpec 2 ● Filters to run select tests – RSpec.configure do |c| c.filter_run :focus => true end ● Model specs: – be_a_new(Array) ● Controller specs: – integrate_views → render_views – assigns[:key]=val → assigns(:key,val) (deprecated)
  • 19. RSpec 2 cont'd ● View specs: – response → rendered – assigns[:key]=val → assign(:key, val) (Req) ● Routing specs: – route_for is gone – route_to, be_routable (also in Rspec 1.3)
  • 21. RVM ● multiple, isolated Rubies ● can have different gemsets each Install: http://rvm.beginrescueend.com/rvm/install/ As User or System-Wide > rvm install ruby-1.8.7 > rvm gemset create rails3 > rvm ruby-1.8.7@rails3 > rvm info
  • 22. RVM Settings ● System: /etc/rvmrc ● User: ~/.rvmrc ● Project: .rvmrc in project(s) root > mkdir workspace > cd workspace > echo “ruby-1.8.7@rails3” > .rvmrc > cd ../workspace > rvm info > gem list
  • 23. Installing gems ● Do NOT use sudo with RVM!!! ● gems are specific to the Ruby and the gemset > rvm info → make sure we're on gemset “rails3” > gem install rails > gem install rspec-rails > gem list
  • 24. Rails 3: rails command ● Replaces script/* – new – console – dbconsole – generate – server
  • 25. Let's do some coding Demo
  • 26. > rails generate rspec:install > rails generate model User first_name:string last_name:string email:string
  • 27. TDD Cycle ● Start user story ● Experiment ● Write test ● Write code ● Refactor ● Finish user story
  • 28. Structure of Tests Setup Expected value Actual value Verification: actual == expected? Teardown
  • 29. Good Tests are... Compact Responsible for testing one concern only Fast DRY
  • 30. RSpec Verifications should respond_to should be_nil → works with any ? method (so-called “predicates”) should be_valid should_not be_nil; should_not be_valid lambda {...}.should change(), {}, .from().to(), .by() should ==, equal, eq, be
  • 31. RSpec Structure before, before(:each), before(:all) after, after(:each), after(:all) describe do...end, nested it do... end
  • 32. RSpec Subject describe Address do it “must have a street” do a = Address.new a.should_not be_valid a.errors.on(:street).should_not be_nil end #subject { Address.new } # Can be omitted if .new # on same class as in describe it “must have a street” do should_not be_valid # should is called on # subject by default subject.errors.on(:street).should_not be_nil end end
  • 33. RSpec2 ● https://github.com/rspec/rspec-rails ● http://blog.davidchelimsky.net/ ● http://relishapp.com/rspec ● More modular, some API changes Gemspec file, for Rails 3: group :development, :test do gem 'rspec-rails', "~> 2.0.1" end
  • 34. Models: What to test? Validation Rules Associations Any custom method Association Proxy Methods
  • 35. Let's do some coding Exercise...
  • 36. Story Exercise #1 A User object must have a first and last name. A User object can construct a full name from the first and last name. A User object has an optional middle name. A User object returns a full name including, if present, the middle name.
  • 37. RSpec ==, eql, equal obj.should == 5 5 == 5 obj.should eq(5) obj.should equal(5) 5.equal 5 obj.should be(5) Use == or eq Object Equality vs. Identity Unless you know you eql, == compare values need something else equal, === compare objects, classes Warning! Do not use != with RSpec. Use should_not instead.
  • 38. RSpec should change lambda {…}.should change... expect {…}.to change... expect { Person.create }.to change(Person, :count).from(0).to(1) lambda { @bob.addresses.create(:street => “...”) }.should change{@bob.addresses.count}.by(1)
  • 40. Test Models for... ● validation ● side-effects before/after saving ● associations ● association proxy methods ● scopes, custom finders ● nested attributes ● observers ● custom methods
  • 42. How to Test for Validations? it 'requires X' do n = Model.new n.should_not be_valid n.errors[:x].should_not be_empty end ● Instantiate object with invalid property ● Check for not valid? ● Check for error on right attribute
  • 43. Check for Side Effects
  • 44. Model Callbacks Requirement: Callbacks: Default a value before before_save saving after_save Send an email after after_destroy saving ... Post to a URL on delete ...
  • 45. How to test Callbacks? Through their Side Effects: ● Set up object in state before callback ● Trigger callback ● Check for side effect it 'encrypts password on save' do n = User.new n.should_not be_valid n.errors.on(:x).should_not be_nil end
  • 46. How are Callbacks triggered? Callback Trigger event before_validation valid? after_validation valid? before_save save, create after_save save, create before_create create after_create create before_destroy destroy after_destroy destroy after_find (see docs) find after_initialize (see docs) new
  • 48. Model Associations Requirement: has_many Entities have has_one relationships belongs_to Given an object, I want to find all related objects has_many :through
  • 49. Tables and Associations class Customer < AR::Base class Order < AR::Base has_many :orders belongs_to :customer ... ... end end Source: Rails Guides, http://guides.rubyonrails.org/association_basics.html
  • 50. Migrations and Associations create_table :addresses do |t| class Address < AR::Base t.belongs_to :person belongs_to :person # same as: ... # t.integer :person_id end ... end class Person < AR::Base create_table :people do |t| has_many :addresses ... end ... end
  • 51. Association Methods belongs_to :person has_many :assets .person .assets .person = .assets << .build_person() .assets = [...] .create_person() .assets.delete(obj,..) .assets.clear .assets.empty? .assets.create(...) .assets.build(...) .assets.find(...) Source: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
  • 53. Indices for Associations Rule: Any database column that can occur in a WHERE clause should have an index create_table :addresses do |t| t.belongs_to :person # same as: # t.integer :person_id ... end add_index :addresses, :person_id
  • 54. How to test for Associations? ● Are the association methods present? ● Checking for one is enough. ● No need to “test Rails” unless using associations with options ● Check that method runs, if options used it “has many addresses” do p = Person.new p.should respond_to(:addresses) end
  • 55. Association Options Ordering has_many :people, :order => “last_name ASC” Class Name belongs_to :customer, :class_name => “Person” Foreign Key has_many :messages, :foreign_key => “recipient_id” Conditions has_many :unread_messages, :class_name => “Message”, :conditions => {:read_at => nil}
  • 56. How to test Assn's with Options? ● Set up a non-trivial data set. ● Verify that it's non-trival. ● Run association method having options ● Verify result it “sorts addresses by zip” do p = Factory(:person) # Factory for addrs with zip 23456, 12345 Address.all.should == [addr1, addr2] p.addresses.should == [addr2, addr1] p.should respond_to(:addresses) end
  • 57. More Association Options Joins has_many :popular_items, :class_name => “Item”, :include => :orders, :group => “orders.customer_id”, :order => “count(orders.customer_id) DESC”
  • 58. Exercise A User can have 0 or more Addresses. A User's Address must have a street, city, state and zip. A User's Address can have an optional 2-letter country code. If the country is left blank, it should default to “US” prior to saving. Extra Credit: State is required only if country is “US” or “CA” Zip must be numerical if country is “US”
  • 60. Controllers Controllers are pass-through entities Mostly boilerplate—biz logic belongs in the model Controllers are “dumb” or “skinny” They follow a run-of-the mill pattern: the Controller Formula
  • 61. Controller RESTful Actions Display methods (“Read”) GET: index, show, new, edit Update method PUT Create method POST Delete method DELETE
  • 62. REST? Representational State Transfer All resource-based applications & API's need to do similar things, namely: create, read, update, delete It's a convention: no configuration, no ceremony superior to CORBA, SOAP, etc.
  • 63. RESTful rsources in Rails map.resources :people (in config/routes.rb) people_path, people_url “named route methods” GET /people → “index” action POST /people → “create” action new_person_path, new_person_url GET /people/new → “new” action edit_person_path, edit_person_url GET /people/:id/edit → “edit” action with ID person_path, person_url GET /people/:id → “show” action with ID PUT /people/:id → “update” action with ID DELETE /people/:id → “destroy” action with ID
  • 64. Read Formula Find data, based on parameters Assign variables Render
  • 65. Reads Test Pattern Make request (with id of record if a single record) Check Rendering correct template redirect status code content type (HTML, JSON, XML,...) Verify Variable Assignments required by view
  • 66. Create/Update Formula Update: Find record from parameters Create: Instantiate new model object Assign form fields parameters to model object This should be a single line It is a pattern, the “Controller Formula” Save Handle success—typically a redirect Handle failure—typically a render
  • 67. Create/Update Test Pattern Make request with form fields to be created/upd'd Verify Variable Assignments Verify Check Success Rendering Verify Failure/Error Case Rendering Variables Verify HTTP Verb protection
  • 68. How much test is too much? Test anything where the code deviates from defaults, e.g. redirect vs. straight up render These tests are not strictly necessary: response.should be_success response.should render_template('new') Test anything required for the application to proceed without error Speficially variable assignments Do test error handling code!
  • 69. How much is enough? Notice: No view testing so far. Emphasize behavior over display. Check that the application handles errors correctly Test views only for things that could go wrong badly incorrect form URL incorrect names on complicated forms, because they impact parameter representation
  • 70. View Testing RSpec controllers do not render views (by default) Test form urls, any logic and input names Understand CSS selector syntax View test requires set up of variables another reason why there should only be very few variables between controller and view some mocks here are OK
  • 71. RSpec 2 View Update ● should have_tag is gone ● Use webrat matchers: – Add “webrat” to Gemfile – Add require 'webrat/core/matchers' to spec_helper.rb – matcher is should have_selector(“css3”) ● response is now rendered ● rendered.should have_selector(“css3”)
  • 73. Object level All three create a “mock” object. mock(), stub(), double() at m = mock(“A Mock”) the Object level are synonymous m = stub(“A Mock”) m = double(“A Mock”) Name for error reporting
  • 74. Using Mocks Mocks can have method m = mock(“A Mock”) stubs. m.stub(:foo) They can be called like m.foo => nil methods. Method stubs can return m.stub(:foo). values. and_return(“hello”) m.foo => “hello” Mocks can be set up with built-in method stubs. m = mock(“A Mock”, :foo => “hello”)
  • 75. Message Expectations Mocks can carry message m = mock(“A Mock”) expectations. should_receive expects a m.should_receive(:foo) single call by default Message expectations can m.should_receive(:foo). return values. and_return(“hello”) Can expect multiple calls. m.should_receive(:foo). twice m.should_receive(:foo). exactly(5).times
  • 76. Argument Expectations m = mock(“A Mock”) Regular expressions m.should_receive(:foo). with(/ello/) Hash keys with(hash_including( :name => 'joe')) Block with { |arg1, arg2| arg1.should == 'abc' arg2.should == 2 }
  • 77. Partial Mocks jan1 = Time.civil(2010) Replace a method on an existing class. Time.stub!(:now). and_return(jan1) Add a method to an Time.stub!(:jan1). existing class. and_return(jan1)
  • 78. Dangers of Mocks
  • 79. Problems Non-DRY Simulated API vs. actual API Maintenance Simulated API gets out of sync with actual API Tedious to remove after “outside-in” phase Leads to testing implementation, not effect Demands on integration and exploratory testing higher with mocks. Less value per line of test code!
  • 80. So what are they good for? External services API's System services Time I/O, Files, ... Sufficiently mature (!) internal API's Slow queries Queries with complicated data setup
  • 81. TDD with Webservices Amazon RSS Feed SimpleRSS gem Nokogiri XML parser gem FakeWeb mocks
  • 83. Step 2: Proof of Concept
  • 84. Step 3: Specs & Refactor
  • 85. Exercise: Step 3 ● Using TDD techniques with – FakeWeb – mocks ● Build up a Product model with: – a fetch class method returning an array of Product instances – instance methods for: ● title, description, link ● image_url (extracted from description) ● Refactor controller & view to use Product model
  • 86. Reference ● https://github.com/wolframarnold/Efficient- TDD-Rails3 ● Class Videos: http://goo.gl/Pe6jE ● Rspec Book ● https://github.com/rspec/rspec-rails ● http://blog.davidchelimsky.net/ ● http://relishapp.com/rspec