Slide 1

Slide 1 text

4 Steps to Faster Rails Test Tom Clements Senior Developer, On The Beach tom-clements.com | github.com/seenmyfate @Seenmyfate

Slide 2

Slide 2 text

A question How long does it take to run your unit tests?

Slide 3

Slide 3 text

Let's look at some numbers $ time rake spec 31.486 seconds $ time bundle exec rspec spec 15.741 seconds $ time rspec spec 14.932 seconds

Slide 4

Slide 4 text

And what am I testing?

Slide 5

Slide 5 text

Nothing! # spec/nowt_spec.rb require 'spec_helper' describe "Nothing" do # nada end 0 examples, 0 failures 15.741 seconds total

Slide 6

Slide 6 text

So how can we make this better?

Slide 7

Slide 7 text

Here's some common suggestions Don't Test Get an SSD Use Spork

Slide 8

Slide 8 text

these solutions are just masking the problem And the first part of that problem is spec_helper

Slide 9

Slide 9 text

spec helper is the best way to write really slow painful specs Gary Bernhardt

Slide 10

Slide 10 text

With spec_helper - 15.741 seconds Without:

Slide 11

Slide 11 text

0.186 seconds # spec/some_spec.rb # require 'spec_helper' describe "Nothing" do # nada end 0 examples, 0 failures 0.186 seconds total

Slide 12

Slide 12 text

So here's the question

Slide 13

Slide 13 text

For our unit tests - Why are we loading the entire rails environment? Because our business logic is in our active record models And we need rails to run tests against classes that inherit from ActiveRecord::Base Because we're breaking SRP

Slide 14

Slide 14 text

Single Responsibility Principle A class should only have one reason to change

Slide 15

Slide 15 text

4 Steps Extract business logic into modules Extract domain objects into classes Mixin and delegate Test in isolation

Slide 16

Slide 16 text

An example class Basket < ActiveRecord::Base has_many :basket_items def total_discount basket_items.collect(&:discount).sum end end

Slide 17

Slide 17 text

An example require 'spec_helper' describe Basket do context "total_discount" do let(:basket) { Basket.create! } let(:basket_items) { [BasketItem.create!(:discount => 10), BasketItem.create!(:discount => 20) ]} it "should return the total discount" do basket = Basket.create! basket.basket_items = basket_items basket.total_discount.should == 30 end end end

Slide 18

Slide 18 text

7.092 seconds to run time rspec spec . Finished in 0.24435 seconds 1 example, 0 failures rspec spec 7.092 total

Slide 19

Slide 19 text

Extract behaviour into modules module DiscountCalculator def total_discount basket_items.collect(&:discount). inject(:+) end end

Slide 20

Slide 20 text

Mixin class Basket < ActiveRecord::Base has_many :basket_items include DiscountCalculator end

Slide 21

Slide 21 text

And the test require 'discount_calculator' class FakeBasket include DiscountCalculator end describe DiscountCalculator do context "#total_discount" do it "should return the total discount" do basket = FakeBasket.new basket_items = [stub(:discount => 10), stub(:discount => 20)] basket.stub(:basket_items) { basket_items } basket.total_discount.should eq 30 end end end

Slide 22

Slide 22 text

0.350 seconds to run time rspec spec . Finished in 0.00121 seconds 1 example, 0 failures rspec spec 0.350 total

Slide 23

Slide 23 text

Extract domain objects into classes class DiscountCalculator def total_discount(items) items.collect(&:discount).inject(:+) end end

Slide 24

Slide 24 text

And delegate class Basket < ActiveRecord::Base has_many :basket_items def total_discount DiscountCalculator.new. total_discount(basket_items) end end

Slide 25

Slide 25 text

And the test require 'discount_calculator' describe DiscountCalculator do context "#total_discount" do let(:items) { [stub(:discount => 10), stub(:discount => 20)] } it "should return the total discount" do calculator = DiscountCalculator.new calculator.total_discount(items).should eq 30 end end end

Slide 26

Slide 26 text

0.342 seconds to run time rspec spec . Finished in 0.00101 seconds 1 example, 0 failures rspec spec 0.342 total

Slide 27

Slide 27 text

The benefits Higher Cohesion Lightning fast tests Happier developing Try it out github.com/seenmyfate

Slide 28

Slide 28 text

Further viewing/reading @coreyhaines - confreaks.net/videos/641-gogaruco2011-fast-rails- tests @garybernhardt - destroyallsoftware.com @martinfowler - objectmentor.com/resources/articles/srp.pdf

Slide 29

Slide 29 text

4 Steps to Faster Rails Test Tom Clements Senior Developer, On The Beach tom-clements.com | github.com/seenmyfate @Seenmyfate