15

私はActiveRecord Model、PricePackageを持っています。それはbefore_createコールバックを持っています。このコールバックでは、第三者APIを使用してリモート接続を行います。私は工場の女の子を使用しており、このAPIをスタブして、テスト中に新しいファクトリが構築されたときにリモート呼び出しが行われないようにしたいと考えています。アクティブレコードを模擬してスタブする方法factory_girlでコールバックbefore_createコールバック

私はモックとスタブにRspecを使用しています。私がいる問題は、RSpecの方法は私のfactories.rb以内に利用できないということである

モデル:

class PricePackage < ActiveRecord::Base 
    has_many :users 
    before_create :register_with_3rdparty 

    attr_accessible :price, :price_in_dollars, :price_in_cents, :title 


    def register_with_3rdparty 
     return true if self.price.nil? 

     begin 
      3rdPartyClass::Plan.create(
      :amount => self.price_in_cents, 
      :interval => 'month', 
      :name => "#{::Rails.env} Item #{self.title}", 
      :currency => 'usd', 
      :id => self.title) 
     rescue Exception => ex 
      puts "stripe exception #{self.title} #{ex}, using existing price" 
      plan = 3rdPartyClass::Plan.retrieve(self.title) 
      self.price_in_cents = plan.amount 
      return true 
     end 
    end 

工場:

#PricePackage 
Factory.define :price_package do |f| 
    f.title "test_package" 
    f.price_in_cents "500" 
    f.max_domains "20" 
    f.max_users "4" 
    f.max_apps "10" 
    f.after_build do |pp| 
    # 
    #heres where would like to mock out the 3rd party response 
    # 
    3rd_party = mock() 
    3rd_party.stub!(:amount).price_in_cents 
    3rdPartyClass::Plan.stub!(:create).and_return(3rd_party) 
    end 
end 

私が取得するかどうかはわかりませんrspecモックとスタブヘルパーは私のfactories.rbにロードされ、これはこれを処理する最良の方法ではないかもしれません。

+1

をそれはfにするのはいいことですそれを裏返して、人々が与える答えの1つにそれを割り当てます。それをせずに単に蒸発する –

+0

'after_build'の' pp.stub(:register_with_3rdparty){true} 'はエラーを発生させますか? – lulalala

答えて

19

VCRの宝石の著者として、私はこれらのような場合にお勧めすると思います。私は確かHTTP依存コードをテストするためにそれをお勧めしますが、あなたの設計に根本的な問題があると思います。 TDD(テストドリブン開発)は設計規律であることを忘れてはいけません。簡単にテストすることが苦痛であると、デザインに関することが分かります。あなたのテストの痛みに耳を傾ける!

この場合、お客様のモデルは第三者API呼び出しを行うことはできません。それは、単一責任の原則にかなり重大な違反です。モデルは、一部のデータの検証と永続性を担うべきですが、これは間違いありません。

代わりに、第三者API呼び出しをオブザーバに移動することをお勧めします。パット・マドックス(Pat Maddox)は、観察者がSRP(単一責任の原則)に違反することなく、どのようにゆるやかに物を結びつけることができるか、そしてどのようにしてテストを行うかについて、非常に簡単に、またデザインを向上させる方法について議論しているgreat blog postです。

これをオブザーバに移動したら、単体テストでオブザーバを無効にして(オブザーバの特定のテストを除く)、プロダクションと統合テストで有効にしておきます。 Patのno-peeping-tomsプラグインを使用してこれを手助けすることができます。レール3.1の場合は、easily enable/disable observersを許可するActiveModelに組み込まれたnew functionalityをチェックアウトする必要があります。

+0

API呼び出しが*永続性(ActiveResourceのようなもの)であれば? *モデルではない*が不適切なようです。 –

+0

もちろん、データがHTTP APIを介して永続化されている場合は、モデルの主な責任はモデルにあります(モデルにスーパークラスまたはモジュールが混在している必要があります)。私が「第三者APIコール」と言ったことに注目してください。私はあなたの永続性が第3の部分のAPIであるとは決して考えません。 –

+0

あなたの意見がわかります。説明をありがとう。 –

-1

まあ、まず、あなたが「モックとスタブ」のモデルとの関係を推測ファクトリー・ガールの言語

ではありません、私はあなたが別のオブジェクトファクトリを構築したいと思うことだね、そのプロパティを設定します、それらを関連付けます。

#PricePackage 
Factory.define :price_package do |f| 
    f.title "test_package" 
    f.price_in_cents "500" 
    f.max_domains "20" 
    f.max_users "4" 
    f.max_apps "10" 
    f.after_build do |pp| 
    f.3rdClass { Factory(:3rd_party) } 
end 

Factory.define :3rd_party do |tp| 
    tp.price_in_cents = 1000 
end 

うまくいけば、私は関係を判読不能にしなかったでしょう。

+0

'price_package'とサードパーティのものとの間にはデータの関連付けがありません。モデルの例を追加しました。これは3rdpartyのapiが 'before_create'のコールバック内で呼び出されることを証明するのに役立ちます。 register_with_3rdpartyメソッド内でその部分をスタブして模擬したいと思います。工場の女の子が新しい 'price_package'ファクトリが作成されるたびに直接接続しないようにします。 – kevzettler

1

VCRの宝石をチェックアウトします(https://www.relishapp.com/myronmarston/vcr)。あなたのテストスイートのHTTPインタラクションを記録し、あなたのためにそれらを再生します。実際にサードパーティのAPIにHTTP接続するための要件を削除します。私はこれを、手動で対話を嘲笑するよりもはるかに単純なアプローチであることがわかった。ここでは、Foursquareライブラリを使用した例を示します。

VCR.config do |c| 
    c.cassette_library_dir = 'test/cassettes' 
    c.stub_with :faraday 
end 

describe Checkin do 
    it 'must check you in to a location' do 
    VCR.use_cassette('foursquare_checkin') do 
     Skittles.checkin('abcd1234') # Doesn't actually make any HTTP calls. 
            # Just plays back the foursquare_checkin VCR 
            # cassette. 
    end 
    end 
end 
0

FactoryGirlは、オブジェクトの属性をスタブアウトすることができ、多分それはあなたを助けることができる:私はカプセル化の面で魅力を見ることができますが

# Returns an object with all defined attributes stubbed out 
stub = FactoryGirl.build_stubbed(:user) 

あなたはFactoryGirl's rdocs

1

でより多くの情報を見つけることができ、あなたの工場内でサードパーティのスタブが発生する必要はありません。

工場でカプセル化する代わりに、RSpecテストの開始時に単に定義することができます。これにより、テストの前提が明確になり、開始時に明記されます(デバッグ時に非常に役立ちます)

PricePlanを使用するテストの前に、希望の応答をセットアップしてから、第三者.createメソッド:

before(:all) do 
    3rd_party = mock('ThirdParty') 
    3rdPartyClass::Plan.stub(:create).and_return(true) 
end 

これで、メソッドを呼び出すことはできますが、リモート呼び出しは無効になります。

*第三者のスタブが元のオブジェクト(:price_in_cents)にいくつかの依存関係を持っているようですが、正確な依存関係についてはわかっていません。適切なスタブ(または必要な場合)が何かを推測できません。

+0

は動作していないようです:TypeError: #はクラス/モジュールではありません – avioing

+0

このアプローチの別の問題は...通常、私は全面的にきれいなテスト。しかし、このモデルは直接的または間接的に使用される可能性が高いため、1回のテストでこれを行う必要があります。したがって、カプセル化を探している@kevzettler(工場内) – avioing

0

私は同じ正確な問題を抱えていました。これらの内容で/サポートを仕様に

がファイル3rdparty.rbを追加:オブザーバーの議論はさておき、ここでは(それがスタートだと/大幅に改善されなければならないことができます)私のために働いていたものです(それはアプローチかもしれません) :

RSpec.configure do |config| 
    config.before do 
    stub(3rdPartyClass::Plan).create do 
    [add stuff here] 
    end 
    end 
end 

そして、あなたのspec_helper.rbこの持っていることを確認してください:あなたが質問に報奨金を割り当てるときはさておき、その恵みは関係なく、あなたが割り当てたかどうかのあなたの評判から取得されるように

Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 
関連する問題