Railsのテストを高速にする

http://www.youtube.com/watch?v=bNn6M2vqxHE

1 comment | 1 point | by WazanovaNews 約7時間前 edited


1000
Jshiike 約7時間前 edited | ▲upvoteする | link

Airbnbのテスト:巻き込み力のある人がポジティブな変化をもたらす」でLou Kosakが、依存関係のないユニットテストを実現するのに参考にしたというCorey HainesのGoGaRuCo 2011での講演です。

テストの実行時間改善のために依存関係を解消するという意味では、一番読込みに時間のかかる3rd partyエンジン、つまりRailsとの関係を切り離すのが効果が大きい。

1  class ShoppingCart < ActiveRecord::Base
2    has_many :shopping_cart_products, dependent: :destroy
3    has_many :products, :through => :shopping_cart_products
4
5    def total_price
6      products.map(&:price).inject(0, &:+)
7    end
8  end

のコードに対して、メソッドを切り出して、標準のRubyコードにする。

1  class ShoppingCart < ActiveRecord::Base
2    include ShoppingCartExtensions::CalculatesTotalPrice  
3
4    has_many :shopping_cart_products, dependent: :destroy
5    has_many :products, :through => :shopping_cart_products
6
7  end
1  module ShoppingCartExtensions
2    module CalculatesTotalPrice
3      def total_price
4        products.map(&:price).inject(0, &:+)
5      end
6    end
7  end

そして、これをテスト。ダミーのカートを用意し、自分のmodelに依存するかたちにする。

1  require 'shopping_cart_extensions/calculates_total_price'
2
3  class DummyCartWithProducts
4    include ShoppingCartExtensions::CalculatesTotalPrice
5  end
6
7  describe "Calculating Total Price" do
8    it "return the total price of the products" do
9      cart = DummyCartWithProducts.new
10     products = [stub(price: 5), stub(price: 10)]
11     cart.stub(:products) { products }
12     cat.total_price,should == 15
13   end
14 end

もう一つのテクニックとして、Active Recordへの依存がイヤであれば、別のオブジェクトにデリゲートすることもできる。

1  class ShoppingCart < ActiveRecord::Base
2    #  include ShoppingCartExtensions::CalculatesTotalPrice  
3
4    has_many :shopping_cart_products, dependent: :destroy
5    has_many :products, :through => :shopping_cart_products
6
7    def total_price
8      ShoppingCartExtensions::CalculatePrices.new.total_price(products)
9    end
10  
11 end
1  module ShoppingCartExtensions
2    class CalculatesPrices
3      def total_price(things_with_prices)
4        things_with_prices.map(&:price).inject(0, &:+)
5      end
6    end
7  end

#rails #ruby #テスト

1000
Back