2016-04-24 16 views
1

データを設定し、私は次のテストを持っています。 3つのブロックがあります。最初のものは他の2つとは異なりshouldaを使用しません。RSpecの+ shoulda:

post :create, product: attrsbeforeブロックを使用しない場合、最初のテストは期待通りに失敗します。しかし、そこにbeforeブロックを置くと、最初のテストは失敗しますが、他の2つは失敗します。私は製品名に対して一意性の検証をしていますが、工場でシーケンスを使用しているので問題はありません。

どうすればよいですか? rspecとshouldaマッチャーが同時に存在する場合に、テスト用のデータを一般的にどのようにセットアップすればよいですか?

describe "when user logged in" do 
    before(:each) do 
    login_user #logged in user is available by calling @user 
    end 

    context "POST create" do 
    context "with valid attributes" do 
     let!(:profile) { create(:profile, user: @user) } 
     let!(:industry) { create(:industry) } 
     let!(:attrs) { attributes_for(:product, user_id: @user.id, industry_ids: [ industry.id ]).merge(
      product_features_attributes: [attributes_for(:product_feature)], 
      product_competitions_attributes: [attributes_for(:product_competition)], 
      product_usecases_attributes: [attributes_for(:product_usecase)] 
     ) } 

     it "saves the new product in the db" do 
     expect{ post :create, product: attrs }.to change{ Product.count }.by(1) 
     end 

     #If I don't use this the 2 tests below fail. If I use it, then the test above fails. 
     # before do 
     # post :create, product: attrs 
     # end 

     it { is_expected.to redirect_to product_path(Product.last) } 
     it { is_expected.to set_flash.to('Product got created!') } 
    end 
    end 
end 

工場

factory :product, class: Product do 
    #name { Faker::Commerce.product_name } 
    sequence(:name) { |n| "ABC_#{n}" } 
    company { Faker::Company.name } 
    website { 'https://example.com' } 
    oneliner { Faker::Lorem.sentence } 
    description { Faker::Lorem.paragraph } 
    user 
end 
+0

異なるコンテキストブロックの使用を検討しましたか?これにより、前のブロックを別々にすることができます。 –

+0

Petr、やったよ。私はちょうどいいやり方が何なのか分かりません。その上に、問題の根本を理解したいと思います。 –

答えて

1

あなたはそれを両方の方法を使用することはできません。 beforeでテストしているメソッドを実行すると、再度実行してProductカウントが変更されているかどうかを確認することはできません。以前に実行しなかった場合は、その例で実行する必要があります。したがって、1つのライナー形式のis_expectedは使用できません。

選択肢の様々なものがあります。ここでは、メソッドの実行をすべての例に組み込んだものを示します。

describe "when user logged in" do 
    before(:each) do 
    login_user #logged in user is available by calling @user 
    end 

    describe "POST create" do 
    subject(:create) { post :create, product: attrs } 
    context "with valid attributes" do 
     let!(:profile) { create(:profile, user: @user) } 
     let!(:industry) { create(:industry) } 
     let!(:attrs) { attributes_for(:product, user_id: @user.id, industry_ids: [ industry.id ]).merge(
      product_features_attributes: [attributes_for(:product_feature)], 
      product_competitions_attributes: [attributes_for(:product_competition)], 
      product_usecases_attributes: [attributes_for(:product_usecase)] 
     ) } 

     it "saves the new product in the db" do 
     expect{ create }.to change{ Product.count }.by(1) 
     end 

     it("redirects") { expect(create).to redirect_to product_path(Product.last) } 
     it("flashes") { expect(create).to set_flash.to('Product got created!') } 
    end 
    end 
end 
+0

ピーター、答えてくれてありがとう!さまざまなソリューションが組み合わされたり、比較されたりする優れたリソースを知っていませんか?私は、 'let'、' subject'、 'before'、' rspec matchers'と 'shoulda matchers'についてたくさん読んだことがありますが、すべてのリソースがそれらを孤立して説明しました。それらを一緒に使用することになるとかなり混乱することがあります。 –

+0

ピーター、plsも私の前のコメントを参照してください。ブロックが '{}} 'のように" {(create).to redirect_to product_path(Product.last)} "をリダイレクトするように渡すことはできますか?何らかの理由で、私のために 'do' +' end'だけで動作します。 –

+0

あなたは中括弧について絶対に正しいです。メソッドの呼び出しでカッコを省略してブロックを追加することはできません。ブロックに 'do''end'を使うか、かっこを入れなければなりません。私はそれに応じて答えを更新しました。それは私が私がコメントすることができるはず感じる何かが –