$30 off During Our Annual Pro Sale. View Details »

Practical Faster Testing (With Rails)

Practical Faster Testing (With Rails)

From the Scottish Ruby Conference Fringe event 2012.

A short talk with some tips and advice on how to get faster tests with Rails.

Jon Rowe

July 01, 2012
Tweet

More Decks by Jon Rowe

Other Decks in Programming

Transcript

  1. PRACTICAL
    FASTER TESTING
    JON ROWE
    WITH RAILS*
    Sunday, 1 July 12

    View Slide

  2. PRACTICAL
    FASTER TESTING
    JON ROWE
    *SORT OF...
    WITH RAILS*
    Sunday, 1 July 12

    View Slide

  3. DISCLAIMER
    I’m not going to offer tips on how to
    speed up your existing legacy tests.
    Sunday, 1 July 12

    View Slide

  4. WHY FAST?
    Sunday, 1 July 12

    View Slide

  5. WHY FAST?
    We know this right?
    Sunday, 1 July 12

    View Slide

  6. WHY FAST?
    We know this right?
    Shorten the feedback cycle
    Sunday, 1 July 12

    View Slide

  7. WHY FAST?
    We know this right?
    Shorten the feedback cycle
    Focus
    Sunday, 1 July 12

    View Slide

  8. WHY FAST?
    We know this right?
    Shorten the feedback cycle
    Focus
    Actually write tests
    Sunday, 1 July 12

    View Slide

  9. WHY FAST?
    We know this right?
    Shorten the feedback cycle
    Focus
    Actually write tests
    “Feel Awesome”@kytrinyx
    Sunday, 1 July 12

    View Slide

  10. SAD TRUTH
    Sunday, 1 July 12

    View Slide

  11. SAD TRUTH
    No test is faster than no tests
    Sunday, 1 July 12

    View Slide

  12. SAD TRUTH
    No test is faster than no tests
    F5DD
    Sunday, 1 July 12

    View Slide

  13. SAD TRUTH
    No test is faster than no tests
    F5DD
    Laziness is easy
    Sunday, 1 July 12

    View Slide

  14. SO WHY SLOW?
    Sunday, 1 July 12

    View Slide

  15. SO WHY SLOW?
    “Your code makes it slow”
    Sunday, 1 July 12

    View Slide

  16. SO WHY SLOW?
    “Your code makes it slow”
    Your tests make it slow
    Sunday, 1 July 12

    View Slide

  17. SO WHY SLOW?
    “Your code makes it slow”
    Your tests make it slow
    And…
    Sunday, 1 July 12

    View Slide

  18. FACT
    Rails is what makes your tests slow
    Sunday, 1 July 12

    View Slide

  19. FACT
    Rails is what makes your tests slow
    But that’s not Rails’ fault
    Sunday, 1 July 12

    View Slide

  20. FACT
    Rails is what makes your tests slow
    But that’s not *all* Rails’ fault
    Sunday, 1 July 12

    View Slide

  21. THE RAILS WAY
    Sunday, 1 July 12

    View Slide

  22. THE RAILS WAY
    Auto load *everything*
    Sunday, 1 July 12

    View Slide

  23. THE RAILS WAY
    Auto load *everything*
    Integration test
    Sunday, 1 July 12

    View Slide

  24. THE RAILS WAY
    Auto load *everything*
    Integration test
    Magic!
    Sunday, 1 July 12

    View Slide

  25. THE ENEMY
    require 'spec_helper'
    require 'rspec/rails'
    require 'rails'
    Sunday, 1 July 12

    View Slide

  26. THE ENEMY
    Booting rails is slow
    Sunday, 1 July 12

    View Slide

  27. THE ENEMY
    Booting rails is slow
    Unnecessary for unit testing
    Sunday, 1 July 12

    View Slide

  28. CONTROLLERS
    Sunday, 1 July 12

    View Slide

  29. CONTROLLERS
    When to test
    Sunday, 1 July 12

    View Slide

  30. CONTROLLERS
    When to test
    Don’t?
    Sunday, 1 July 12

    View Slide

  31. CONTROLLERS
    When to test
    Don’t?
    Do?
    Sunday, 1 July 12

    View Slide

  32. class SensibleController < ApplicationController
    def index
    @objects = Record.all
    end
    def new
    @object = Record.new
    end
    def create
    Record.create params[:record]
    redirect_to index_path
    end
    def edit
    @record = Record.find(params[:id])
    end
    def update
    Record.find(params[:id]).update_attributes params[:record]
    redirect_to index_path
    end
    end
    Sunday, 1 July 12

    View Slide

  33. class SensibleController < ApplicationController
    def create
    Record.create! params[:record]
    redirect_to index_path
    rescue
    @record = Record.new params[:record]
    render :new
    end
    def edit
    @record = Record.find(params[:id])
    rescue NotFound
    redirect_to index_path
    end
    def update
    Record.find(params[:id]).update_attributes params[:record]
    redirect_to index_path
    rescue Validations
    @record = Record.new params[:record]
    render :edit
    rescue NotFound
    redirect_to index_path
    end
    end
    Sunday, 1 July 12

    View Slide

  34. class UberDeathController
    def index
    @bugs =
    if params[:q].blank? && params[:c].blank? && params[:m].blank? && params[:order].bla
    Bug.by_gnar_factor.paginate :per_page => @per_page, :page => params[:page]
    elsif params[:q].blank? && !params[:c].blank? && params[:m].blank? && params[:order]
    Bug.for_section(params[:c]).by_gnar_factor.paginate :per_page => @per_page, :page
    elsif params[:c].blank? && !params[:q].blank? && params[:m].blank? && params[:order]
    Bug.search_for(params[:q]).by_gnar_factor.paginate :per_page => @per_page, :page =
    elsif !params[:c].blank? && !params[:q].blank? && params[:m].blank? && params[:order
    Bug.search_for(params[:q]).by_gnar_factor.for_section(params[:c]).paginate :per_pa
    elsif params[:c].blank? && params[:q].blank? && !params[:m].blank? && params[:order]
    Bug.master_bugs.by_gnar_factor.paginate :per_page => @per_page, :page => params[:p
    elsif !params[:c].blank? && params[:q].blank? && !params[:m].blank? && params[:order
    Bug.master_bugs.by_gnar_factor.for_section(params[:c]).paginate :per_page => @per_
    elsif params[:c].blank? && !params[:q].blank? && !params[:m].blank? && params[:order
    Bug.master_bugs.by_gnar_factor.search_for(params[:q]).paginate :per_page => @per_p
    elsif !params[:c].blank? && !params[:q].blank? && !params[:m].blank? && params[:orde
    Bug.master_bugs.by_gnar_factor.search_for(params[:q]).for_section(params[:c]).pagi
    end
    end
    end
    STILL DONT TEST?
    Sunday, 1 July 12

    View Slide

  35. THE TESTS
    Sunday, 1 July 12

    View Slide

  36. THE TESTS
    Integration test
    Sunday, 1 July 12

    View Slide

  37. THE TESTS
    Integration test
    Request Verbs
    Sunday, 1 July 12

    View Slide

  38. THE TESTS
    Integration test
    Request Verbs
    Filters
    Sunday, 1 July 12

    View Slide

  39. THE TESTS
    Integration test
    Request Verbs
    Filters
    Slow
    Sunday, 1 July 12

    View Slide

  40. THE TESTS
    Sunday, 1 July 12

    View Slide

  41. THE TESTS
    Unit test
    Sunday, 1 July 12

    View Slide

  42. THE TESTS
    Unit test
    Skip filters
    Sunday, 1 July 12

    View Slide

  43. THE TESTS
    Unit test
    Skip filters
    Focus on your code
    Sunday, 1 July 12

    View Slide

  44. THE TESTS
    Unit test
    Skip filters
    Focus on your code
    Let Rails work
    Sunday, 1 July 12

    View Slide

  45. INTEGRATION
    require 'spec_helper'
    describe ExampleWithModelController do
    describe 'post :create' do
    subject { post :create }
    it "creates a model" do
    expect { subject }.to change(RandomModel,:count).by(1)
    end
    it "redirects away" do
    subject.should redirect_to '/'
    end
    end
    end
    Sunday, 1 July 12

    View Slide

  46. INTEGRATION
    require 'spec_helper'
    describe ExampleWithModelController do
    describe 'post :create' do
    before { RandomModel.stub(:create) }
    subject { post :create }
    it "creates a RandomModel" do
    RandomModel.should_receive(:create)
    subject
    end
    it "redirects away" do
    subject.should redirect_to '/'
    end
    end
    end
    Sunday, 1 July 12

    View Slide

  47. UNIT
    require 'action_controller'
    require 'app/controllers/application_controller'
    require 'app/controllers/example_with_model_controller'
    Sunday, 1 July 12

    View Slide

  48. class RandomModel; end
    describe ExampleWithModelController do
    describe '#create' do
    let(:controller) { described_class.new }
    before do
    controller.request = Struct.new(:parameters).new
    controller.stub(:redirect_to)
    end
    subject { controller.create }
    it "creates a RandomModel" do
    RandomModel.should_receive(:create)
    subject
    end
    it "redirects to away" do
    controller.should_receive(:redirect_to).with('/')
    subject
    end
    end
    end
    Sunday, 1 July 12

    View Slide

  49. RESULTS (x100)
    ============================
    unit
    ============================
    0.000000 0.000000 1.140000 ( 1.149252)
    ============================
    integration
    ============================
    0.000000 0.010000 2.510000 ( 2.498981) (stubbed)
    0.000000 0.000000 3.070000 ( 3.175934) (full stack)
    Sunday, 1 July 12

    View Slide

  50. RESULTS (x1000)
    ============================
    unit x1000
    ============================
    0.000000 0.020000 3.060000 ( 3.051226)
    ============================
    integration x1000
    ============================
    0.010000 0.020000 8.380000 ( 8.382151) (stubbed)
    0.010000 0.020000 13.160000 ( 15.175460) (full stack)
    Sunday, 1 July 12

    View Slide

  51. YEAH...*
    *Caveats
    Sunday, 1 July 12

    View Slide

  52. YEAH...*
    Controller stubbing (view isolation)
    *Caveats
    Sunday, 1 July 12

    View Slide

  53. YEAH...*
    Controller stubbing (view isolation)
    What about my filters?
    *Caveats
    Sunday, 1 July 12

    View Slide

  54. MODELS
    require 'active_record'
    config = YAML.load(File.open("config/database.yml"))["test"]
    ActiveRecord::Base.establish_connection(config)
    require 'app/models/random_model'
    0.310000 0.070000 0.380000 ( 0.540731)
    require 'spec_helper'
    1.300000 0.220000 1.520000 ( 1.756871)
    Sunday, 1 July 12

    View Slide

  55. MODELS
    So not much improvement
    Sunday, 1 July 12

    View Slide

  56. MODELS
    So not much improvement
    DB calls?
    Sunday, 1 July 12

    View Slide

  57. MODELS
    So not much improvement
    DB calls?
    How can we make these faster?
    Sunday, 1 July 12

    View Slide

  58. MODULAR
    Sunday, 1 July 12

    View Slide

  59. MODULAR
    Injecting your objects
    Sunday, 1 July 12

    View Slide

  60. MODULAR
    Injecting your objects
    Code faster?
    Sunday, 1 July 12

    View Slide

  61. MODULAR
    Injecting your objects
    Code faster?
    Slower? No!
    Sunday, 1 July 12

    View Slide

  62. MODULAR
    Injecting your objects
    Code faster?
    Slower? No!
    Tests faster? YES!
    Sunday, 1 July 12

    View Slide

  63. MODULAR
    Injecting your objects
    Code faster?
    Slower? No!
    Tests faster? YES!
    Less to load
    Sunday, 1 July 12

    View Slide

  64. class ChangeStock
    def initialize(item)
    @item = item
    end
    def delta
    quantity - @item.quantity
    end
    def to(quantity)
    if quantity > @item.quantity
    @item.fee_generator.charge_increase delta
    end
    @item.set_quantity quantity
    @item.save!
    end
    end
    Sunday, 1 July 12

    View Slide

  65. REFACTOR
    Sunday, 1 July 12

    View Slide

  66. REFACTOR
    Separate from the framework
    Sunday, 1 July 12

    View Slide

  67. REFACTOR
    Separate from the framework
    Write your own helpers
    Sunday, 1 July 12

    View Slide

  68. REFACTOR
    Separate from the framework
    Write your own helpers
    Mocking/Stubbing
    Sunday, 1 July 12

    View Slide

  69. BE SENSIBLE
    Sunday, 1 July 12

    View Slide

  70. BE SENSIBLE
    Test when it makes sense
    Sunday, 1 July 12

    View Slide

  71. BE SENSIBLE
    Test when it makes sense
    No hard and fast rules
    Sunday, 1 July 12

    View Slide

  72. BE SENSIBLE
    Test when it makes sense
    No hard and fast rules
    Consider the value
    Sunday, 1 July 12

    View Slide

  73. BOY SCOUT RULE
    Sunday, 1 July 12

    View Slide

  74. BOY SCOUT RULE
    Always leave code cleaner
    than you found it
    Sunday, 1 July 12

    View Slide

  75. BOY SCOUT RULE
    Always leave tests cleaner
    than you found them
    Sunday, 1 July 12

    View Slide

  76. SUMMARY
    Sunday, 1 July 12

    View Slide

  77. SUMMARY
    Require what you need
    Sunday, 1 July 12

    View Slide

  78. SUMMARY
    Require what you need
    Unit test your units
    Sunday, 1 July 12

    View Slide

  79. SUMMARY
    Require what you need
    Unit test your units
    Abstract away
    Sunday, 1 July 12

    View Slide

  80. SUMMARY
    Require what you need
    Unit test your units
    Abstract away
    Integration/acceptance test framework
    Sunday, 1 July 12

    View Slide

  81. SUMMARY
    Require what you need
    Unit test your units
    Abstract away
    Integration/acceptance test framework
    Don’t boot rails unless needed
    Sunday, 1 July 12

    View Slide

  82. SUMMARY
    Require what you need
    Unit test your units
    Abstract away
    Integration/acceptance test framework
    Don’t boot rails unless needed
    Be sensible!
    Sunday, 1 July 12

    View Slide

  83. THANKS
    Jon Rowe
    @jonrowe (twitter, github, etc)
    http://jonrowe.co.uk
    Sunday, 1 July 12

    View Slide