Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

WHY FAST? Sunday, 1 July 12

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

SAD TRUTH Sunday, 1 July 12

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

SO WHY SLOW? Sunday, 1 July 12

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

THE RAILS WAY Sunday, 1 July 12

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

THE ENEMY Booting rails is slow Sunday, 1 July 12

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

CONTROLLERS Sunday, 1 July 12

Slide 29

Slide 29 text

CONTROLLERS When to test Sunday, 1 July 12

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

THE TESTS Sunday, 1 July 12

Slide 36

Slide 36 text

THE TESTS Integration test Sunday, 1 July 12

Slide 37

Slide 37 text

THE TESTS Integration test Request Verbs Sunday, 1 July 12

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

THE TESTS Sunday, 1 July 12

Slide 41

Slide 41 text

THE TESTS Unit test Sunday, 1 July 12

Slide 42

Slide 42 text

THE TESTS Unit test Skip filters Sunday, 1 July 12

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

MODELS So not much improvement Sunday, 1 July 12

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

MODULAR Sunday, 1 July 12

Slide 59

Slide 59 text

MODULAR Injecting your objects Sunday, 1 July 12

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

REFACTOR Sunday, 1 July 12

Slide 66

Slide 66 text

REFACTOR Separate from the framework Sunday, 1 July 12

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

BE SENSIBLE Sunday, 1 July 12

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

BOY SCOUT RULE Sunday, 1 July 12

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

SUMMARY Sunday, 1 July 12

Slide 77

Slide 77 text

SUMMARY Require what you need Sunday, 1 July 12

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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