4

クラスとスペックがあります。

class Store
  def activate(product_klass, product_id)
    product = product_klass.find(product_id)
    if product.inactive?
      product.update_attribute :active, true
    end
  end
end

describe Store do
  it "should activate an inactive product" do
    product = mock
    product.stub(:inactive?).and_return(true)    
    store = Store.new
    store.activate(22) # 
    product.should be_active
  end
end

仕様の実行に失敗します。私は得る:

Mock received unexpected message :find_by_id with (1)

それを満たすため product.should_receive(:find_by_id).with(1).and_return(product)に、行の前にを追加しますstore.activate(product, 22)。(テストしているメソッドの内部についてテストにあまり知られたくないので、これは間違っているようです)

仕様を再度実行するfalseと、期待される代わりに次の行が返される失敗が発生しますtrue

product.should be_active

つまり、実際には に設定されていないため、返されますfalse。モックに吸収されただけです。product.update_attribute :active, trueactivetrue

たくさんの質問があります。rspec'cing はどのように行うのですか? 代わりにこれをどのようにテストする必要がありましたか? モックとスタブを正しく使用していますか?

どんな助けでも大歓迎です。

4

2 に答える 2

2

アクティベーションロジックはまったく関係ないと思いますStore。で宣言されている場合Product、テストはより自然に見えます。

class Product < ActiveRecord::Base
  def activate
    if inactive?
      update_attribute :active, true
    end
  end
end

describe Product do
  it "should activate an inactive product" do
    product = Product.new
    product.activate 
    product.should be_active
  end
end

Storeそこから、次のようにメソッドを書き直すことができます。

class Store
  def activate(product_klass, product_id)
    product = product_klass.find(product_id)
    product.activate
  end
end

describe Store do
  it "should activate an inactive product" do
    product = mock
    product.should_receive(:activate)
    product_klass = mock
    product_klass.should_receive(:find).with(22).and_return(product)
    store = Store.new
    store.activate(product_klass, 22)
  end
end
于 2013-05-22T05:44:55.017 に答える
2

Product次のように、製品のアクティベーションがモデル上にある必要があるという事実について、@padde に同意します。

class Product < ActiveRecord::Base
  def activate
    if inactive?
      update_attribute :active, true
    end
  end
end

ただし、テストをリファクタリングして、Rspec の標準プラクティスに合わせてインライン化します。

describe Product do
  context "after activating" do   # Human readable situation of the test
    let(:product) { Product.new.activate }
    subject { product }           # Make product the subject of the test

    it { should be_active }       # Product should be active
  end
end

そしてStoreテスト:

describe Store do
  context "when activating a product" do
    let(:product)       { mock }
    let(:store)         { Store.new }

    before do
      product_klass = double                 # Stub the product class, don't mock
      product_klass.stub(:find) { product }  # We want to test product here, not the class
      store.activate(product_klass, 22)
    end

    subject { product }                      # product is the subject again

    it { should_receive(:activate) }         # it should receive the activate message
  end
end

product_klassこの場合、それは実際にテストしたいものではないため、への期待を削除しました。別のテストとしてそれを好むかもしれません。

を使用してlet、標準的な方法でテストsubjectcontext整理し、rspec がクラスの人間に優しいドキュメントを作成するなどのいくつかのきちんとしたことを実行できるようにします。rspec のベスト プラクティスの詳細については、betterspecsをご覧ください。

于 2013-05-22T09:35:26.627 に答える