工厂女孩协会的问题和 let 的使用:

Issue with factory girl association and use of let:

我有一个奇怪的问题。我为 user/deal has_many/has_many 关系创建了一个工厂 user_deals,但似乎 rspec 现在迫使我断言一个用户,即使我不需要一个用户(确实是用户仅记录在我的数据库中,如果网络访问者登录,则在应用程序中需要)

此测试无效:

context "As NON SIGNED-IN visitor" do       

    describe "HP card has the right content and behavior on small screens (1-column view)", :js => true do      

      let(:subject) { ApplicationController.new } 
      let(:deal_for_german_people)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title of deal for German people") }   
let(:deal_for_german_people2)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title 2 of deal for German people") }  

      before do
        resize_window_to_mobile            
      end  

      after do
        resize_window_default
        end

      it "plus and minus icons behave correctly" do
        visit root_path
        # user sees on mobile the little "plus" icon by default
        expect(page).to have_css('.card-detail-opener', visible: true)  
    end

end

但如果我添加 create(:user) 并创建 "user_deals",测试通过。 但我在上下文中 "non signed in user",那么我为什么要创建一个用户和 user_deals 呢?

context "As NON SIGNED-IN visitor" do       

    describe "HP card has the right content and behavior on small screens (1-column view)", :js => true do      

      let(:subject) { ApplicationController.new }
      let(:user)                    { create(:user,
                                                user_country_name: 'Germany')  }  
      let(:deal_for_german_people)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title of deal for German people") }   
let(:deal_for_german_people2)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title 2 of deal for German people") }  

      before do
        resize_window_to_mobile
        create(:user_deal, user: user, deal: deal_for_german_people)
        create(:user_deal, user: user, deal: deal_for_german_people2)
      end  

      after do
        resize_window_default
        end

      it "plus and minus icons behave correctly" do
        visit root_path
        # user sees on mobile the little "plus" icon by default
        expect(page).to have_css('.card-detail-opener', visible: true)  
    end

end

此外,仍在调查这种怪异的原因,我发现如果我在测试开始时不使用 "let:" ,而是在 th 之前创建变量 @deals ...做块,它无需创建用户即可工作(但我想使用 rspec "let" 来 DRY 并应用 rspec best practices

describe "HP card has the right content and behavior on small screens (1-column view)", :js => true do      


    let(:subject) { ApplicationController.new }     

      before do
        resize_window_to_mobile

        @deal_for_german_people = FactoryGirl.build( :deal,

                                                      :country          => "Germany",
                                                      :title            => "title of deal for German people").save(validate: false) 
@deal_for_german_people2 = FactoryGirl.build( :deal,

                                                      :country          => "Germany",
                                                      :title            => "title 2 of deal for German people").save(validate: false) 
      end  

      after do
        resize_window_default
        end

      it "plus and minus icons behave correctly" do  
        visit root_path
        expect(page).to have_css('.card-detail-opener', visible: true)
      end 

型号

class Deal < ActiveRecord::Base
  has_many   :user_deals,           dependent:  :destroy
  has_many   :users,                through:    :user_deals
end

class User < ActiveRecord::Base
  has_many :user_deals          
  has_many :deals,                through: :user_deals
end

class UserDeal < ActiveRecord::Base
  belongs_to :user,         :foreign_key => 'user_id'
  belongs_to :deal,         :foreign_key => 'deal_id'
end

tableuser_deals

的结构
# Table name: user_deals
#
#  id                 :integer          not null, primary key
#  user_id            :integer
#  deal_id            :integer
#  number_of_clicks   :integer          default(0)
#  created_at         :datetime
#  updated_at         :datetime

工厂用户

FactoryGirl.define do
  factory :user do
    sequence(:email) { |n| "person#{n}@example.com" }

    password "vddfdf"
    password_confirmation "vddfdf"

    confirmed_at Time.now
    confirmation_token nil

    trait :superadmin do
      role :superadmin
    end

    trait :with_deals do 
      after(:create) do |user|
        create_list(:deal, 5, user: user)
      end
    end
  end
end

工厂user_deal

FactoryGirl.define do
  factory :user_deal do
    association :user
    association :deal

    trait :few_clicks do
      number_of_clicks 1
    end

    trait :many_clicks do
      number_of_clicks 4
    end
  end
end

我的猜测是您期待两者

let(:deal_for_german_people) ...
let(:deal_for_german_people2) ...

it 块之前创建。但是,在您的第一个失败场景中,情况并非如此,因为您没有在 it 块的任何部分中 call/use 这些变量。您应该将 let 变量视为等待成为 运行 的代码。

我想你想要的是 let! 即刻 运行。

以下可能会起作用

context "As NON SIGNED-IN visitor" do       
  describe "HP card has the right content and behavior on small screens (1-column view)", :js => true do      
    ...
    let!(:deal_for_german_people)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title of deal for German people") }   
    let!(:deal_for_german_people2)  { create(:deal_skips_validate,   
                                                :country          => "Germany",
                                                :title            => "title 2 of deal for German people") }  
    ...
  end
end