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.

D79fc498d7a5b2ce12180890247476f0?s=128

Jon Rowe

July 01, 2012
Tweet

Transcript

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

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

    1 July 12
  3. DISCLAIMER I’m not going to offer tips on how to

    speed up your existing legacy tests. Sunday, 1 July 12
  4. WHY FAST? Sunday, 1 July 12

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

  6. WHY FAST? We know this right? Shorten the feedback cycle

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

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

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

    Focus Actually write tests “Feel Awesome”@kytrinyx Sunday, 1 July 12
  10. SAD TRUTH Sunday, 1 July 12

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

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

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

    Laziness is easy Sunday, 1 July 12
  14. SO WHY SLOW? Sunday, 1 July 12

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

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

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

    make it slow And… Sunday, 1 July 12
  18. FACT Rails is what makes your tests slow Sunday, 1

    July 12
  19. FACT Rails is what makes your tests slow But that’s

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

    not *all* Rails’ fault Sunday, 1 July 12
  21. THE RAILS WAY Sunday, 1 July 12

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

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

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

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

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

  27. THE ENEMY Booting rails is slow Unnecessary for unit testing

    Sunday, 1 July 12
  28. CONTROLLERS Sunday, 1 July 12

  29. CONTROLLERS When to test Sunday, 1 July 12

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

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

  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
  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
  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
  35. THE TESTS Sunday, 1 July 12

  36. THE TESTS Integration test Sunday, 1 July 12

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

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

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

    July 12
  40. THE TESTS Sunday, 1 July 12

  41. THE TESTS Unit test Sunday, 1 July 12

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

  43. THE TESTS Unit test Skip filters Focus on your code

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

    Let Rails work Sunday, 1 July 12
  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
  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
  47. UNIT require 'action_controller' require 'app/controllers/application_controller' require 'app/controllers/example_with_model_controller' Sunday, 1 July

    12
  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
  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
  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
  51. YEAH...* *Caveats Sunday, 1 July 12

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

  53. YEAH...* Controller stubbing (view isolation) What about my filters? *Caveats

    Sunday, 1 July 12
  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
  55. MODELS So not much improvement Sunday, 1 July 12

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

    12
  57. MODELS So not much improvement DB calls? How can we

    make these faster? Sunday, 1 July 12
  58. MODULAR Sunday, 1 July 12

  59. MODULAR Injecting your objects Sunday, 1 July 12

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

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

    July 12
  62. MODULAR Injecting your objects Code faster? Slower? No! Tests faster?

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

    YES! Less to load Sunday, 1 July 12
  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
  65. REFACTOR Sunday, 1 July 12

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

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

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

    Sunday, 1 July 12
  69. BE SENSIBLE Sunday, 1 July 12

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

    12
  71. BE SENSIBLE Test when it makes sense No hard and

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

    fast rules Consider the value Sunday, 1 July 12
  73. BOY SCOUT RULE Sunday, 1 July 12

  74. BOY SCOUT RULE Always leave code cleaner than you found

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

    them Sunday, 1 July 12
  76. SUMMARY Sunday, 1 July 12

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

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

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

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

    away Integration/acceptance test framework Sunday, 1 July 12
  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
  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
  83. THANKS Jon Rowe @jonrowe (twitter, github, etc) http://jonrowe.co.uk Sunday, 1

    July 12