SlideShare ist ein Scribd-Unternehmen logo
1 von 92
Advanced Testing
  RubyEnRails Conference ’09
Goals

• Share our experiences about testing
• Make you start thinking critical about testing
  like an advanced tester
• Unit testing with RSpec
• Integration testing with Cucumber
Student Master of
               Computer Science



@edwin_v   Edwin Vlieg
                            Course: Testing
                             Techniques
Student Master of
               Computer Science



@edwin_v   Edwin Vlieg
                            Course: Testing
                                      5
                             Techniques
• Web application written in Ruby on Rails
• Create and send invoices online
• More than 16.000 invoices sent
• Short development cycle: 2 months
• API for web developers
• More info: www.moneybird.com
“A donkey does not knock
himself to the same stone twice”
It is okay to write code that doesn’t work...

         But never ‘cap deploy’ it!
So we started testing
Unit testing
Software testing
... is a technical process performed by executing
a product in a controlled environment, following
  a specified procedure, with the intent of
measuring the quality of the software product by
demonstrating deviations of the requirements.
Unit testing

• Test individual units of code
• A unit is the smallest testable piece of
  code: methods of classes
• E.g. methods of controllers and models
TDD / BDD

Test before you write a single piece of code
TDD / BDD

Test before you write a single piece of code

Can be a nice approach, but requires a lot of
  discipline and might not fit your needs,
       but know how it can help you!
Example
                 Recurring invoices


Calculate the next occurence of a recurring invoice.



  Start date          Current occurence         Next occurence




        Jan    Febr          Mar          Apr       May
First version


def next_occurence(today)
  today + 1.month
end
RSpec-t

describe "Date calculation" do
  it "should return the next month" do
    next_occurence(Date.parse('2009-01-01')).should ==
         Date.parse('2009-02-01')
  end
end


                               You ain’t testing this,
                             because it looks too easy
Done! It is green.
Customer: “I want to bill every
         last day of the month”



‘2009-01-31’ + 1.month = ‘2009-02-28’
‘2009-02-28’ + 1.month = ‘2009-03-28’
‘2009-01-31’ + 2.months = ‘2009-03-31’
Boundary Value Analysis
Find the boundaries of the input and test them
Boundary Value Analysis
Find the boundaries of the input and test them

        Start       Current       Next
       2009-01-31   2009-01-31   2009-02-28
       2009-01-31   2009-02-28   2009-03-31
       2009-01-31   2009-03-31   2009-04-30
       2009-01-30   2009-01-30   2009-02-28
       2009-01-30   2009-02-28   2009-03-30
       2009-01-30   2009-03-30   2009-04-30
       2009-01-29   2009-01-29   2009-02-28
       2009-01-29   2009-02-28   2009-03-29
       2009-01-29   2009-03-29   2009-04-29
       2009-01-28   2009-01-28   2009-02-28
       2009-01-28   2009-02-28   2009-03-28
       2009-01-28   2009-03-28   2009-04-28
Boundary Value Analysis
Find the boundaries of the input and test them

        Start       Current       Next
       2009-01-31   2009-01-31   2009-02-28
       2009-01-31   2009-02-28   2009-03-31
       2009-01-31   2009-03-31   2009-04-30
       2009-01-30   2009-01-30   2009-02-28
       2009-01-30   2009-02-28   2009-03-30
       2009-01-30   2009-03-30   2009-04-30
       2009-01-29   2009-01-29   2009-02-28
       2009-01-29   2009-02-28   2009-03-29
       2009-01-29   2009-03-29   2009-04-29
       2009-01-28   2009-01-28   2009-02-28
       2009-01-28   2009-02-28   2009-03-28
       2009-01-28   2009-03-28   2009-04-28
Boundary Value Analysis
Find the boundaries of the input and test them

        Start       Current       Next
       2009-01-31   2009-01-31   2009-02-28
       2009-01-31   2009-02-28   2009-03-31
       2009-01-31   2009-03-31   2009-04-30
       2009-01-30   2009-01-30   2009-02-28
       2009-01-30   2009-02-28   2009-03-30
       2009-01-30   2009-03-30   2009-04-30
       2009-01-29   2009-01-29   2009-02-28
       2009-01-29   2009-02-28   2009-03-29
       2009-01-29   2009-03-29   2009-04-29
       2009-01-28   2009-01-28   2009-02-28
       2009-01-28   2009-02-28   2009-03-28
       2009-01-28   2009-03-28   2009-04-28
Boundary Value Analysis
Find the boundaries of the input and test them

        Start       Current       Next
       2009-01-31   2009-01-31   2009-02-28
       2009-01-31   2009-02-28   2009-03-31
       2009-01-31   2009-03-31   2009-04-30
       2009-01-30   2009-01-30   2009-02-28
       2009-01-30   2009-02-28   2009-03-30
       2009-01-30   2009-03-30   2009-04-30
       2009-01-29   2009-01-29   2009-02-28
       2009-01-29   2009-02-28   2009-03-29
       2009-01-29   2009-03-29   2009-04-29
       2009-01-28   2009-01-28   2009-02-28
       2009-01-28   2009-02-28   2009-03-28
       2009-01-28   2009-03-28   2009-04-28
Create tests for all boundary values...

      ...and improve your code!
The solution

def self.calculate_next_occurence(start_date, current_date)
  next_date = current_date + 1.month
  while start_date.day != next_date.day and
        next_date != next_date.end_of_month
    next_date = next_date.tomorrow
  end
  next_date
end
“Testing shows the presence,
  not the absence of bugs”
                        - Dijkstra
So, why test?
So, why test?



     When working with dates, it takes a while
      before the Boundary Values occure.
Isolation
Isolate for productivity

• Test isolated units of code, assuring they are working
• No need to bother about it later
• No need to find all edge cases in the user interface:
  F5 syndrom
Isolate your tests

• Don’t test code that is tested elsewhere
• Only make sure it is used well in the code you’re testing
• The solution: mocks and stubs
Mock objects are simulated objects that mimic
the behavior of real objects in controlled ways




  A stub is a piece of code used to stand in
  for some other programming functionality
Very hard
Method A   Method B
                        work
Method B   Very hard
                        work




Method A
Method B   Very hard
                        work




Method A    Mock
Method B   Very hard
                           work




Method A        Stub




       Mock
Method B          Very hard
                                  work




Method A




       Mock              Stub
Mock   Stub
How to isolate?
def create_invoice(contact)
  invoice = Invoice.new
  invoice.contact_id = contact.id
  invoice.details_attributes =
    [{ :description => "RER09", :price => 79 }]
  invoice.save
end


class Invoice < ActiveResource::Base
  self.site = "https://account.moneybird.com"
end
How to isolate?
def create_invoice(contact)
  invoice = Invoice.new
  invoice.contact_id = contact.id
  invoice.details_attributes =
    [{ :description => "RER09", :price => 79 }]
  invoice.save
end


class Invoice < ActiveResource::Base
  self.site = "https://account.moneybird.com"
end
                          ActiveResource is tested elsewhere +
                                   very slow to test
describe "MoneyBird API" do
  it "should create the invoice" do




    create_invoice(contact_mock).should be_true
  end
end
describe "MoneyBird API" do
  it "should create the invoice" do




   contact_mock = mock(:contact)
   contact_mock.should_receive(:id).and_return(3)

    create_invoice(contact_mock).should be_true
  end
end
describe "MoneyBird API" do
  it "should create the invoice" do




   Invoice.should_receive(:new).and_return(invoice_mock)

   contact_mock = mock(:contact)
   contact_mock.should_receive(:id).and_return(3)

    create_invoice(contact_mock).should be_true
  end
end
describe "MoneyBird API" do
  it "should create the invoice" do
   invoice_mock = mock(:invoice)
   invoice_mock.stub!(:contact_id=)
   invoice_mock.stub!(:details_attributes=)
   invoice_mock.should_receive(:save).and_return(true)

   Invoice.should_receive(:new).and_return(invoice_mock)

   contact_mock = mock(:contact)
   contact_mock.should_receive(:id).and_return(3)

    create_invoice(contact_mock).should be_true
  end
end
Isolation

Isolated    A    B
Isolation

Isolated        A       B




Not isolated    A   B
Isolation

Isolated        A       B




Not isolated    A   B
                                 Double
                                    =
                            Extra test work +
                            maintenance work!
What to test?
          Everything!


But never too much!! Be critical
Downside of testing

• Every test gives overhead
• Testing the obvious takes time
• Ruby library, CRUD controllers
Downside of testing

 • Every test gives overhead
 • Testing the obvious takes time
 • Ruby library, CRUD controllers
But what if suddenly:
         BigDecimal("10.03").to_f != 10.03
We use (very) fat models




                           So we unit test them
RSpec

• All business logic in models
• Models tested with RSpec
• Basic data model in fixtures
Fixtures?
• Models make use of the database
• Sometimes data in de database is needed
  for the model to work
• Fixtures are easy to manage on the
  filesystem (+ version control)
• Fixtures can be used for bootstrapping
  application: rake db:fixtures:load
first_contact:
  company: bluetools
  name: Edwins company
  contact_name: Edwin Vlieg
  address1: Street 82
  zipcode: 7541 XA
  city: Enschede
  country: NL
  send_method: mail
  created_at: <%= 60.days.ago.to_s :db %>
  contact_hash: 1
second_contact:
  company: bluetools
  name: BlueTools B.V.
  address1: Postbus 123
  zipcode: 7500EA
  city: Enschede
  country: NL
  send_method: post
  contact_hash: 2
first_contact:
  company: bluetools
                                Unique identifier for
  name: Edwins company          ‘row’ in database.
  contact_name: Edwin Vlieg     Don’t set the id column!!
  address1: Street 82
  zipcode: 7541 XA
  city: Enschede
  country: NL
  send_method: mail
  created_at: <%= 60.days.ago.to_s :db %>
  contact_hash: 1
second_contact:
  company: bluetools
  name: BlueTools B.V.
  address1: Postbus 123
  zipcode: 7500EA
  city: Enschede
  country: NL
  send_method: post
  contact_hash: 2
first_contact:
  company: bluetools
                                Unique identifier for
  name: Edwins company          ‘row’ in database.
  contact_name: Edwin Vlieg     Don’t set the id column!!
  address1: Street 82
  zipcode: 7541 XA
  city: Enschede
  country: NL
  send_method: mail
  created_at: <%= 60.days.ago.to_s :db %>
  contact_hash: 1
second_contact:
  company: bluetools
  name: BlueTools B.V.         Yes, you can use Ruby!
  address1: Postbus 123
  zipcode: 7500EA
  city: Enschede
  country: NL
  send_method: post
  contact_hash: 2
first_contact:
  company: bluetools
                                Unique identifier for
  name: Edwins company          ‘row’ in database.
  contact_name: Edwin Vlieg     Don’t set the id column!!
  address1: Street 82
  zipcode: 7541 XA
  city: Enschede
  country: NL
  send_method: mail
  created_at: <%= 60.days.ago.to_s :db %>
  contact_hash: 1
second_contact:
  company: bluetools
  name: BlueTools B.V.         Yes, you can use Ruby!
  address1: Postbus 123
  zipcode: 7500EA
  city: Enschede
  country: NL
  send_method: post
  contact_hash: 2     Reference to unique identifier
                      in companies table
We don’t test controllers and views.
Because,

• Controllers are just plain CRUD actions
• Views just contain HTML
• And both are tested with integration
  testing in Cucumber
Integration testing
The goal
• Test the full stack: models, views and
  controllers
• Behaviour driven approach
Our story
So we started testing
Feature: Homepage
    Visitors at the homepage should get clear information about
    our product and be able to create an account

    Scenario: Create a new free account
        When I visit the homepage
         And I follow "pricing"
         And I follow "Signup"
         And I fill in the following:
            | Company name          | Test company          |
            | Your fullname         | Edwin Vlieg           |
            | E-mail                | test@test.com          |
            | company_domain        | testcompany           |
            | Username              | Edwin                 |
            | Password              | testtest              |
            | Password confirmation | testtest              |
         And I press "Create your account"
        Then a company with name "Test company" should exist
         And an e-mail with subject "Welcome to MoneyBird" should have been sent
         And I should see "Thanks for signing up!"
Cucumber stories

• Each line is parsed and matched on a step
• Step contains actual execution of code
• 3 types of steps: Given, When and Then.
homepage.feature

        When I follow "Signup"




             webrat_steps.rb

When /^I follow "([^"]*)"$/ do |link|
  click_link(link)
end
Reuse step definitions




                       In MoneyBird:

              40 step definitions

      695 steps executed
40 steps

• 21   Webrat steps (Cucumber default)
•4     Common steps (DB & Mailer)
• 15   Domain specific steps
Webrat

• Webrat gives you control over the user
  interface (almost) like an end user has
• But doesn’t emulate a browser like
  Selenium or Watir (= no JavaScript)
Web-what?
  Web browser



     Apache



Mongrel / Passenger              Webrat



              ActionController
When I follow "Signup"

1. Locate the link on the page
<a href="/signup">Signup</a>

<a href="/signup" title="Signup"><img src="..." /></a>

<a href="/signup" id="Signup" title="Click to signup"><img src="..."></a>
When I follow "Signup"

1. Locate the link on the page
<a href="/signup">Signup</a>

<a href="/signup" title="Signup"><img src="..." /></a>

<a href="/signup" id="Signup" title="Click to signup"><img src="..."></a>
When I follow "Signup"

1. Locate the link on the page
<a href="/signup">Signup</a>

<a href="/signup" title="Signup"><img src="..." /></a>

<a href="/signup" id="Signup" title="Click to signup"><img src="..."></a>




2. ‘Click’ the link
Grab the ‘href’ attribute from the element and feed it to the
Rails application. HTML is replaced: next search of element
will be in new page.
Cucumber & Webrat
Cucumber comes with default Webrat steps to:
• Visit pages
• Click links
• Fill in forms
• Submit forms
• Assert the content of the page
Testability
Webrat doesn’t execute JavaScript, so write
unobtrusive JavaScript to keep it testable.
Testability
  Webrat doesn’t execute JavaScript, so write
  unobtrusive JavaScript to keep it testable.

                      HTML
<a href="/popup.html" id="open">Open popup</a>


                 Link opens popup, even without JavaScript
Testability
  Webrat doesn’t execute JavaScript, so write
  unobtrusive JavaScript to keep it testable.

                         HTML
<a href="/popup.html" id="open">Open popup</a>


                    Link opens popup, even without JavaScript

                        JavaScript
             $('open').click(...);


          JavaScript adds extra behaviour to link
Testability
          Webrat doesn’t execute JavaScript, so write
          unobtrusive JavaScript to keep it testable.

                                 HTML
   <a href="/popup.html" id="open">Open popup</a>


                            Link opens popup, even without JavaScript

                                JavaScript
                     $('open').click(...);
Rails 3

                  JavaScript adds extra behaviour to link
Common steps
Database steps:
Given I've created an invoice with invoice id "2008-0100"

Then a contact with name "Test company" should exist



Mailer steps:
Then an e-mail with subject "Invoice 2008-0100 from
Bluetools" should have been sent




         Full source: http://pastie.org/667777
Database steps

 Given I've created an invoice with invoice id "2008-0100"




     Should create a new invoice with invoice id
“2008-0100”, but what about the rest of the attributes?
FactoryGirl
     • Makes it easy to create records in the
         database
     • Define default attributes for a record:
Factory.define :contact do |c|
  c.company_id      { |c| Company.find_by_domain($subdomain).id }
  c.name            "Test company"
  c.contact_name    "Edwin Vlieg"
  c.address1        "Hengelosestraat 538"
  c.zipcode         "7500AG"
  c.city            "Enschede"
  c.country         "NLD"
  c.email           "test@moneybird.nl"
end
FactoryGirl
• Easy instantiation of records in database:
     Factory(:contact, :name => "Foobar")



    Factory name          Override default
                            attributes



              More information:
 http://github.com/thoughtbot/factory_girl
Downside
           When I follow "Signup"



       What if we want to change “Signup”
              to “Create account”?



Test breaks, but application is functionally correct.
Conclusions
Testing takes time,
so be critical about what to test
Don’t stare blindly at TDD or BDD


 Creating mesmerizing products needs creativity,
this doesn’t always fit into the ‘test-first’ approach,


   but know the ideas behind the approaches,
       so you can use them when needed!
Creating a good test environment takes time:
   it just doesn’t end with RSpec or Cucumber



Mocks, stubs, fixtures and factories are your friends!
Write testable code



Keep the units small for easy unit testing



   Keep the HTML clean for webrat
Thanks!

Weitere ähnliche Inhalte

Andere mochten auch

MoneyBird Gouden Factuur 2009
MoneyBird Gouden Factuur 2009MoneyBird Gouden Factuur 2009
MoneyBird Gouden Factuur 2009Edwin Vlieg
 
Frontend ontwikkeling
Frontend ontwikkelingFrontend ontwikkeling
Frontend ontwikkelingEdwin Vlieg
 
Docker from a team perspective
Docker from a team perspectiveDocker from a team perspective
Docker from a team perspectiveEdwin Vlieg
 
implantaçao de PMO em Lisarb
implantaçao de PMO em Lisarbimplantaçao de PMO em Lisarb
implantaçao de PMO em LisarbMarco Coghi
 
ETG at GPC
ETG at GPCETG at GPC
ETG at GPCipga
 
Proyecto 6 To Semestre
Proyecto 6 To SemestreProyecto 6 To Semestre
Proyecto 6 To Semestreguest3782515
 
Ciao Sorrento Leaflet
Ciao Sorrento   LeafletCiao Sorrento   Leaflet
Ciao Sorrento LeafletEuro Taxi
 
Atlas av skapelsen. swedish (svenska) docx
Atlas av skapelsen. swedish (svenska) docxAtlas av skapelsen. swedish (svenska) docx
Atlas av skapelsen. swedish (svenska) docxHarunyahyaSwedish
 
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLM
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLMJefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLM
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLMMaría Isabel Rodriguez
 
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...Deltares
 
summer_program_guide_2016_nsymca_3
summer_program_guide_2016_nsymca_3summer_program_guide_2016_nsymca_3
summer_program_guide_2016_nsymca_3Misty Vinson Spitzer
 
Livro receitas revisao-6_low
Livro receitas revisao-6_lowLivro receitas revisao-6_low
Livro receitas revisao-6_lowROSAMARrir
 
El Amor Verdadero No Pide Recompensas
El Amor Verdadero No Pide RecompensasEl Amor Verdadero No Pide Recompensas
El Amor Verdadero No Pide Recompensasroxy
 
Administración de Consorcios
Administración de ConsorciosAdministración de Consorcios
Administración de Consorciosgrupog.asociados
 
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...Carlos Flores
 
vitamina D en la infancia y adolescencia
vitamina D en la infancia y adolescenciavitamina D en la infancia y adolescencia
vitamina D en la infancia y adolescenciatu endocrinologo
 

Andere mochten auch (20)

MoneyBird Gouden Factuur 2009
MoneyBird Gouden Factuur 2009MoneyBird Gouden Factuur 2009
MoneyBird Gouden Factuur 2009
 
Frontend ontwikkeling
Frontend ontwikkelingFrontend ontwikkeling
Frontend ontwikkeling
 
Docker from a team perspective
Docker from a team perspectiveDocker from a team perspective
Docker from a team perspective
 
implantaçao de PMO em Lisarb
implantaçao de PMO em Lisarbimplantaçao de PMO em Lisarb
implantaçao de PMO em Lisarb
 
ETG at GPC
ETG at GPCETG at GPC
ETG at GPC
 
Proyecto 6 To Semestre
Proyecto 6 To SemestreProyecto 6 To Semestre
Proyecto 6 To Semestre
 
Henrob Overview
Henrob OverviewHenrob Overview
Henrob Overview
 
Ciao Sorrento Leaflet
Ciao Sorrento   LeafletCiao Sorrento   Leaflet
Ciao Sorrento Leaflet
 
Atlas av skapelsen. swedish (svenska) docx
Atlas av skapelsen. swedish (svenska) docxAtlas av skapelsen. swedish (svenska) docx
Atlas av skapelsen. swedish (svenska) docx
 
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLM
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLMJefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLM
Jefa de Servicio de Ordenación Académica, Documentación y Evaluación de CLM
 
Ondernemend Sudwest Fryslan juni 2012
Ondernemend Sudwest Fryslan juni 2012Ondernemend Sudwest Fryslan juni 2012
Ondernemend Sudwest Fryslan juni 2012
 
Debra Howard 2016
Debra Howard 2016Debra Howard 2016
Debra Howard 2016
 
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...
DSD-INT 2014 - OpenMI Symposium - An OpenMI composition with channel flow and...
 
summer_program_guide_2016_nsymca_3
summer_program_guide_2016_nsymca_3summer_program_guide_2016_nsymca_3
summer_program_guide_2016_nsymca_3
 
Livro receitas revisao-6_low
Livro receitas revisao-6_lowLivro receitas revisao-6_low
Livro receitas revisao-6_low
 
El Amor Verdadero No Pide Recompensas
El Amor Verdadero No Pide RecompensasEl Amor Verdadero No Pide Recompensas
El Amor Verdadero No Pide Recompensas
 
Manual RIPA resumen
Manual RIPA resumenManual RIPA resumen
Manual RIPA resumen
 
Administración de Consorcios
Administración de ConsorciosAdministración de Consorcios
Administración de Consorcios
 
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...
TEORÍAS DE REALIDAD AUMENTADA Y TECNOLOGÍA SMARTPHONE COMO ESTRATEGIA DE PUBL...
 
vitamina D en la infancia y adolescencia
vitamina D en la infancia y adolescenciavitamina D en la infancia y adolescencia
vitamina D en la infancia y adolescencia
 

Ähnlich wie Advanced Testing Techniques with Ruby on Rails

Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test communityKerry Buckley
 
An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1Blue Elephant Consulting
 
Getting your mobile test automation process in place - using Cucumber and Cal...
Getting your mobile test automation process in place - using Cucumber and Cal...Getting your mobile test automation process in place - using Cucumber and Cal...
Getting your mobile test automation process in place - using Cucumber and Cal...Niels Frydenholm
 
T# @ Agile Tour Montreal 2009 En
T# @ Agile Tour Montreal 2009 EnT# @ Agile Tour Montreal 2009 En
T# @ Agile Tour Montreal 2009 EnLudovicDubois
 
20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile BoardMarcus Hammarberg
 
Introduzione allo Unit Testing
Introduzione allo Unit TestingIntroduzione allo Unit Testing
Introduzione allo Unit TestingStefano Ottaviani
 
An Introduction To Software Development - Test Driven Development
An Introduction To Software Development - Test Driven DevelopmentAn Introduction To Software Development - Test Driven Development
An Introduction To Software Development - Test Driven DevelopmentBlue Elephant Consulting
 
Serverless in production, an experience report (LNUG)
Serverless in production, an experience report (LNUG)Serverless in production, an experience report (LNUG)
Serverless in production, an experience report (LNUG)Yan Cui
 
Micro Object Testing
Micro Object TestingMicro Object Testing
Micro Object TestingESUG
 
An introduction and future of Ruby coverage library
An introduction and future of Ruby coverage libraryAn introduction and future of Ruby coverage library
An introduction and future of Ruby coverage librarymametter
 
Automock: Interaction-Based Mock Code Generation
Automock: Interaction-Based Mock Code GenerationAutomock: Interaction-Based Mock Code Generation
Automock: Interaction-Based Mock Code GenerationSabrina Souto
 
Test-Driven Development Overview
Test-Driven Development OverviewTest-Driven Development Overview
Test-Driven Development OverviewRob Myers
 
CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!Ortus Solutions, Corp
 
QCon 2014 - How Shutl delivers even faster with Neo4j
QCon 2014 - How Shutl delivers even faster with Neo4jQCon 2014 - How Shutl delivers even faster with Neo4j
QCon 2014 - How Shutl delivers even faster with Neo4jVolker Pacher
 
Serverless in Production, an experience report (cloudXchange)
Serverless in Production, an experience report (cloudXchange)Serverless in Production, an experience report (cloudXchange)
Serverless in Production, an experience report (cloudXchange)Yan Cui
 
Intro to javascript (5:2)
Intro to javascript (5:2)Intro to javascript (5:2)
Intro to javascript (5:2)Thinkful
 
Intro to TDD and BDD
Intro to TDD and BDDIntro to TDD and BDD
Intro to TDD and BDDJason Noble
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Yan Cui
 

Ähnlich wie Advanced Testing Techniques with Ruby on Rails (20)

Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
 
An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1
 
Are we there yet?
Are we there yet?Are we there yet?
Are we there yet?
 
Getting your mobile test automation process in place - using Cucumber and Cal...
Getting your mobile test automation process in place - using Cucumber and Cal...Getting your mobile test automation process in place - using Cucumber and Cal...
Getting your mobile test automation process in place - using Cucumber and Cal...
 
T# @ Agile Tour Montreal 2009 En
T# @ Agile Tour Montreal 2009 EnT# @ Agile Tour Montreal 2009 En
T# @ Agile Tour Montreal 2009 En
 
20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board20 Ideas On How To Improve Your Agile Board
20 Ideas On How To Improve Your Agile Board
 
Introduzione allo Unit Testing
Introduzione allo Unit TestingIntroduzione allo Unit Testing
Introduzione allo Unit Testing
 
An Introduction To Software Development - Test Driven Development
An Introduction To Software Development - Test Driven DevelopmentAn Introduction To Software Development - Test Driven Development
An Introduction To Software Development - Test Driven Development
 
Serverless in production, an experience report (LNUG)
Serverless in production, an experience report (LNUG)Serverless in production, an experience report (LNUG)
Serverless in production, an experience report (LNUG)
 
Introduction to C ++.pptx
Introduction to C ++.pptxIntroduction to C ++.pptx
Introduction to C ++.pptx
 
Micro Object Testing
Micro Object TestingMicro Object Testing
Micro Object Testing
 
An introduction and future of Ruby coverage library
An introduction and future of Ruby coverage libraryAn introduction and future of Ruby coverage library
An introduction and future of Ruby coverage library
 
Automock: Interaction-Based Mock Code Generation
Automock: Interaction-Based Mock Code GenerationAutomock: Interaction-Based Mock Code Generation
Automock: Interaction-Based Mock Code Generation
 
Test-Driven Development Overview
Test-Driven Development OverviewTest-Driven Development Overview
Test-Driven Development Overview
 
CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!
 
QCon 2014 - How Shutl delivers even faster with Neo4j
QCon 2014 - How Shutl delivers even faster with Neo4jQCon 2014 - How Shutl delivers even faster with Neo4j
QCon 2014 - How Shutl delivers even faster with Neo4j
 
Serverless in Production, an experience report (cloudXchange)
Serverless in Production, an experience report (cloudXchange)Serverless in Production, an experience report (cloudXchange)
Serverless in Production, an experience report (cloudXchange)
 
Intro to javascript (5:2)
Intro to javascript (5:2)Intro to javascript (5:2)
Intro to javascript (5:2)
 
Intro to TDD and BDD
Intro to TDD and BDDIntro to TDD and BDD
Intro to TDD and BDD
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)
 

Kürzlich hochgeladen

Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 

Kürzlich hochgeladen (20)

Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 

Advanced Testing Techniques with Ruby on Rails

  • 1. Advanced Testing RubyEnRails Conference ’09
  • 2. Goals • Share our experiences about testing • Make you start thinking critical about testing like an advanced tester • Unit testing with RSpec • Integration testing with Cucumber
  • 3. Student Master of Computer Science @edwin_v Edwin Vlieg Course: Testing Techniques
  • 4. Student Master of Computer Science @edwin_v Edwin Vlieg Course: Testing 5 Techniques
  • 5.
  • 6. • Web application written in Ruby on Rails • Create and send invoices online • More than 16.000 invoices sent • Short development cycle: 2 months • API for web developers • More info: www.moneybird.com
  • 7.
  • 8. “A donkey does not knock himself to the same stone twice”
  • 9. It is okay to write code that doesn’t work... But never ‘cap deploy’ it!
  • 10. So we started testing
  • 12. Software testing ... is a technical process performed by executing a product in a controlled environment, following a specified procedure, with the intent of measuring the quality of the software product by demonstrating deviations of the requirements.
  • 13. Unit testing • Test individual units of code • A unit is the smallest testable piece of code: methods of classes • E.g. methods of controllers and models
  • 14. TDD / BDD Test before you write a single piece of code
  • 15. TDD / BDD Test before you write a single piece of code Can be a nice approach, but requires a lot of discipline and might not fit your needs, but know how it can help you!
  • 16. Example Recurring invoices Calculate the next occurence of a recurring invoice. Start date Current occurence Next occurence Jan Febr Mar Apr May
  • 18. RSpec-t describe "Date calculation" do it "should return the next month" do next_occurence(Date.parse('2009-01-01')).should == Date.parse('2009-02-01') end end You ain’t testing this, because it looks too easy
  • 19. Done! It is green.
  • 20. Customer: “I want to bill every last day of the month” ‘2009-01-31’ + 1.month = ‘2009-02-28’ ‘2009-02-28’ + 1.month = ‘2009-03-28’ ‘2009-01-31’ + 2.months = ‘2009-03-31’
  • 21. Boundary Value Analysis Find the boundaries of the input and test them
  • 22. Boundary Value Analysis Find the boundaries of the input and test them Start Current Next 2009-01-31 2009-01-31 2009-02-28 2009-01-31 2009-02-28 2009-03-31 2009-01-31 2009-03-31 2009-04-30 2009-01-30 2009-01-30 2009-02-28 2009-01-30 2009-02-28 2009-03-30 2009-01-30 2009-03-30 2009-04-30 2009-01-29 2009-01-29 2009-02-28 2009-01-29 2009-02-28 2009-03-29 2009-01-29 2009-03-29 2009-04-29 2009-01-28 2009-01-28 2009-02-28 2009-01-28 2009-02-28 2009-03-28 2009-01-28 2009-03-28 2009-04-28
  • 23. Boundary Value Analysis Find the boundaries of the input and test them Start Current Next 2009-01-31 2009-01-31 2009-02-28 2009-01-31 2009-02-28 2009-03-31 2009-01-31 2009-03-31 2009-04-30 2009-01-30 2009-01-30 2009-02-28 2009-01-30 2009-02-28 2009-03-30 2009-01-30 2009-03-30 2009-04-30 2009-01-29 2009-01-29 2009-02-28 2009-01-29 2009-02-28 2009-03-29 2009-01-29 2009-03-29 2009-04-29 2009-01-28 2009-01-28 2009-02-28 2009-01-28 2009-02-28 2009-03-28 2009-01-28 2009-03-28 2009-04-28
  • 24. Boundary Value Analysis Find the boundaries of the input and test them Start Current Next 2009-01-31 2009-01-31 2009-02-28 2009-01-31 2009-02-28 2009-03-31 2009-01-31 2009-03-31 2009-04-30 2009-01-30 2009-01-30 2009-02-28 2009-01-30 2009-02-28 2009-03-30 2009-01-30 2009-03-30 2009-04-30 2009-01-29 2009-01-29 2009-02-28 2009-01-29 2009-02-28 2009-03-29 2009-01-29 2009-03-29 2009-04-29 2009-01-28 2009-01-28 2009-02-28 2009-01-28 2009-02-28 2009-03-28 2009-01-28 2009-03-28 2009-04-28
  • 25. Boundary Value Analysis Find the boundaries of the input and test them Start Current Next 2009-01-31 2009-01-31 2009-02-28 2009-01-31 2009-02-28 2009-03-31 2009-01-31 2009-03-31 2009-04-30 2009-01-30 2009-01-30 2009-02-28 2009-01-30 2009-02-28 2009-03-30 2009-01-30 2009-03-30 2009-04-30 2009-01-29 2009-01-29 2009-02-28 2009-01-29 2009-02-28 2009-03-29 2009-01-29 2009-03-29 2009-04-29 2009-01-28 2009-01-28 2009-02-28 2009-01-28 2009-02-28 2009-03-28 2009-01-28 2009-03-28 2009-04-28
  • 26. Create tests for all boundary values... ...and improve your code!
  • 27. The solution def self.calculate_next_occurence(start_date, current_date) next_date = current_date + 1.month while start_date.day != next_date.day and next_date != next_date.end_of_month next_date = next_date.tomorrow end next_date end
  • 28. “Testing shows the presence, not the absence of bugs” - Dijkstra
  • 30. So, why test? When working with dates, it takes a while before the Boundary Values occure.
  • 32. Isolate for productivity • Test isolated units of code, assuring they are working • No need to bother about it later • No need to find all edge cases in the user interface: F5 syndrom
  • 33. Isolate your tests • Don’t test code that is tested elsewhere • Only make sure it is used well in the code you’re testing • The solution: mocks and stubs
  • 34. Mock objects are simulated objects that mimic the behavior of real objects in controlled ways A stub is a piece of code used to stand in for some other programming functionality
  • 35. Very hard Method A Method B work
  • 36. Method B Very hard work Method A
  • 37. Method B Very hard work Method A Mock
  • 38. Method B Very hard work Method A Stub Mock
  • 39. Method B Very hard work Method A Mock Stub
  • 40. Mock Stub
  • 41. How to isolate? def create_invoice(contact) invoice = Invoice.new invoice.contact_id = contact.id invoice.details_attributes = [{ :description => "RER09", :price => 79 }] invoice.save end class Invoice < ActiveResource::Base self.site = "https://account.moneybird.com" end
  • 42. How to isolate? def create_invoice(contact) invoice = Invoice.new invoice.contact_id = contact.id invoice.details_attributes = [{ :description => "RER09", :price => 79 }] invoice.save end class Invoice < ActiveResource::Base self.site = "https://account.moneybird.com" end ActiveResource is tested elsewhere + very slow to test
  • 43. describe "MoneyBird API" do it "should create the invoice" do create_invoice(contact_mock).should be_true end end
  • 44. describe "MoneyBird API" do it "should create the invoice" do contact_mock = mock(:contact) contact_mock.should_receive(:id).and_return(3) create_invoice(contact_mock).should be_true end end
  • 45. describe "MoneyBird API" do it "should create the invoice" do Invoice.should_receive(:new).and_return(invoice_mock) contact_mock = mock(:contact) contact_mock.should_receive(:id).and_return(3) create_invoice(contact_mock).should be_true end end
  • 46. describe "MoneyBird API" do it "should create the invoice" do invoice_mock = mock(:invoice) invoice_mock.stub!(:contact_id=) invoice_mock.stub!(:details_attributes=) invoice_mock.should_receive(:save).and_return(true) Invoice.should_receive(:new).and_return(invoice_mock) contact_mock = mock(:contact) contact_mock.should_receive(:id).and_return(3) create_invoice(contact_mock).should be_true end end
  • 48. Isolation Isolated A B Not isolated A B
  • 49. Isolation Isolated A B Not isolated A B Double = Extra test work + maintenance work!
  • 50. What to test? Everything! But never too much!! Be critical
  • 51. Downside of testing • Every test gives overhead • Testing the obvious takes time • Ruby library, CRUD controllers
  • 52. Downside of testing • Every test gives overhead • Testing the obvious takes time • Ruby library, CRUD controllers But what if suddenly: BigDecimal("10.03").to_f != 10.03
  • 53. We use (very) fat models So we unit test them
  • 54. RSpec • All business logic in models • Models tested with RSpec • Basic data model in fixtures
  • 55. Fixtures? • Models make use of the database • Sometimes data in de database is needed for the model to work • Fixtures are easy to manage on the filesystem (+ version control) • Fixtures can be used for bootstrapping application: rake db:fixtures:load
  • 56. first_contact: company: bluetools name: Edwins company contact_name: Edwin Vlieg address1: Street 82 zipcode: 7541 XA city: Enschede country: NL send_method: mail created_at: <%= 60.days.ago.to_s :db %> contact_hash: 1 second_contact: company: bluetools name: BlueTools B.V. address1: Postbus 123 zipcode: 7500EA city: Enschede country: NL send_method: post contact_hash: 2
  • 57. first_contact: company: bluetools Unique identifier for name: Edwins company ‘row’ in database. contact_name: Edwin Vlieg Don’t set the id column!! address1: Street 82 zipcode: 7541 XA city: Enschede country: NL send_method: mail created_at: <%= 60.days.ago.to_s :db %> contact_hash: 1 second_contact: company: bluetools name: BlueTools B.V. address1: Postbus 123 zipcode: 7500EA city: Enschede country: NL send_method: post contact_hash: 2
  • 58. first_contact: company: bluetools Unique identifier for name: Edwins company ‘row’ in database. contact_name: Edwin Vlieg Don’t set the id column!! address1: Street 82 zipcode: 7541 XA city: Enschede country: NL send_method: mail created_at: <%= 60.days.ago.to_s :db %> contact_hash: 1 second_contact: company: bluetools name: BlueTools B.V. Yes, you can use Ruby! address1: Postbus 123 zipcode: 7500EA city: Enschede country: NL send_method: post contact_hash: 2
  • 59. first_contact: company: bluetools Unique identifier for name: Edwins company ‘row’ in database. contact_name: Edwin Vlieg Don’t set the id column!! address1: Street 82 zipcode: 7541 XA city: Enschede country: NL send_method: mail created_at: <%= 60.days.ago.to_s :db %> contact_hash: 1 second_contact: company: bluetools name: BlueTools B.V. Yes, you can use Ruby! address1: Postbus 123 zipcode: 7500EA city: Enschede country: NL send_method: post contact_hash: 2 Reference to unique identifier in companies table
  • 60. We don’t test controllers and views.
  • 61. Because, • Controllers are just plain CRUD actions • Views just contain HTML • And both are tested with integration testing in Cucumber
  • 63. The goal • Test the full stack: models, views and controllers • Behaviour driven approach
  • 65.
  • 66. So we started testing
  • 67. Feature: Homepage Visitors at the homepage should get clear information about our product and be able to create an account Scenario: Create a new free account When I visit the homepage And I follow "pricing" And I follow "Signup" And I fill in the following: | Company name | Test company | | Your fullname | Edwin Vlieg | | E-mail | test@test.com | | company_domain | testcompany | | Username | Edwin | | Password | testtest | | Password confirmation | testtest | And I press "Create your account" Then a company with name "Test company" should exist And an e-mail with subject "Welcome to MoneyBird" should have been sent And I should see "Thanks for signing up!"
  • 68. Cucumber stories • Each line is parsed and matched on a step • Step contains actual execution of code • 3 types of steps: Given, When and Then.
  • 69. homepage.feature When I follow "Signup" webrat_steps.rb When /^I follow "([^"]*)"$/ do |link| click_link(link) end
  • 70. Reuse step definitions In MoneyBird: 40 step definitions 695 steps executed
  • 71. 40 steps • 21 Webrat steps (Cucumber default) •4 Common steps (DB & Mailer) • 15 Domain specific steps
  • 72. Webrat • Webrat gives you control over the user interface (almost) like an end user has • But doesn’t emulate a browser like Selenium or Watir (= no JavaScript)
  • 73. Web-what? Web browser Apache Mongrel / Passenger Webrat ActionController
  • 74. When I follow "Signup" 1. Locate the link on the page <a href="/signup">Signup</a> <a href="/signup" title="Signup"><img src="..." /></a> <a href="/signup" id="Signup" title="Click to signup"><img src="..."></a>
  • 75. When I follow "Signup" 1. Locate the link on the page <a href="/signup">Signup</a> <a href="/signup" title="Signup"><img src="..." /></a> <a href="/signup" id="Signup" title="Click to signup"><img src="..."></a>
  • 76. When I follow "Signup" 1. Locate the link on the page <a href="/signup">Signup</a> <a href="/signup" title="Signup"><img src="..." /></a> <a href="/signup" id="Signup" title="Click to signup"><img src="..."></a> 2. ‘Click’ the link Grab the ‘href’ attribute from the element and feed it to the Rails application. HTML is replaced: next search of element will be in new page.
  • 77. Cucumber & Webrat Cucumber comes with default Webrat steps to: • Visit pages • Click links • Fill in forms • Submit forms • Assert the content of the page
  • 78. Testability Webrat doesn’t execute JavaScript, so write unobtrusive JavaScript to keep it testable.
  • 79. Testability Webrat doesn’t execute JavaScript, so write unobtrusive JavaScript to keep it testable. HTML <a href="/popup.html" id="open">Open popup</a> Link opens popup, even without JavaScript
  • 80. Testability Webrat doesn’t execute JavaScript, so write unobtrusive JavaScript to keep it testable. HTML <a href="/popup.html" id="open">Open popup</a> Link opens popup, even without JavaScript JavaScript $('open').click(...); JavaScript adds extra behaviour to link
  • 81. Testability Webrat doesn’t execute JavaScript, so write unobtrusive JavaScript to keep it testable. HTML <a href="/popup.html" id="open">Open popup</a> Link opens popup, even without JavaScript JavaScript $('open').click(...); Rails 3 JavaScript adds extra behaviour to link
  • 82. Common steps Database steps: Given I've created an invoice with invoice id "2008-0100" Then a contact with name "Test company" should exist Mailer steps: Then an e-mail with subject "Invoice 2008-0100 from Bluetools" should have been sent Full source: http://pastie.org/667777
  • 83. Database steps Given I've created an invoice with invoice id "2008-0100" Should create a new invoice with invoice id “2008-0100”, but what about the rest of the attributes?
  • 84. FactoryGirl • Makes it easy to create records in the database • Define default attributes for a record: Factory.define :contact do |c| c.company_id { |c| Company.find_by_domain($subdomain).id } c.name "Test company" c.contact_name "Edwin Vlieg" c.address1 "Hengelosestraat 538" c.zipcode "7500AG" c.city "Enschede" c.country "NLD" c.email "test@moneybird.nl" end
  • 85. FactoryGirl • Easy instantiation of records in database: Factory(:contact, :name => "Foobar") Factory name Override default attributes More information: http://github.com/thoughtbot/factory_girl
  • 86. Downside When I follow "Signup" What if we want to change “Signup” to “Create account”? Test breaks, but application is functionally correct.
  • 88. Testing takes time, so be critical about what to test
  • 89. Don’t stare blindly at TDD or BDD Creating mesmerizing products needs creativity, this doesn’t always fit into the ‘test-first’ approach, but know the ideas behind the approaches, so you can use them when needed!
  • 90. Creating a good test environment takes time: it just doesn’t end with RSpec or Cucumber Mocks, stubs, fixtures and factories are your friends!
  • 91. Write testable code Keep the units small for easy unit testing Keep the HTML clean for webrat