ActiveRecordRelation,其中唯一id返回对象的方法匹配稍微不同,然后遍历数组并匹配相同的id

时间:2013-09-12 00:26:25

标签: ruby-on-rails ruby activerecord rspec factory-bot

对ruby,rails和单元测试很新,所以欢迎任何其他反馈!

我正在为一个名为#assign_campaign的基于ActiveRecord的模型(联系人)编写一个rspec单元测试。 ContactCampaign是一个将联系人映射到广告系列的关联。这里的想法是,当广告系列被分配时,该联系人的所有其他广告系列都会停用,并且已分配的广告系列也会被激活。

测试失败,因为它声称已分配的广告系列的状态为零而非“有效”(或“无效”)。

似乎归结为“campaign_id”属性上的“where”匹配似乎不会返回相同的对象,就像我遍历contact_campaigns并且也匹配campaign_id一样。

E.g。

contact.contact_campaigns.where(campaign_id: campaign_to_be_assigned.id).first.inspect

抓住我:

< ContactCampaign id:6,contact_id:1,campaign_id:1,status:nil,created_at:“2013-09-11 23:40:07”,updated_at:“2013-09-11 23:40:07 “,current_position:1>

使用object_id:70199917191760

虽然

contact.contact_campaigns.each do |x|
  if x.campaign_id == campaign_to_be_assigned.id
    puts x.inspect
  end
end 

抓住我:

< ContactCampaign id:6,contact_id:1,campaign_id:1,status:“active”,created_at:“2013-09-11 23:40:07”,updated_at:“2013-09-11 23:40 :08“,current_position:1>

使用object_id:70199940110940

这里发生了什么?这是一个ActiveRecord失败,FactoryGirl失败或简单的Ruby失败? ; p

如果我从:

重写assign_campaign
current_contact_campaign = contact_campaigns.where(campaign_id: campaign_id).first
current_contact_campaign.activate
current_contact_campaign.set_current_position(starting_position) unless
  starting_position == 0

致:

contact_campaigns.each do |x|
  if x.campaign_id == campaign_id
    x.activate
    x.set_current_position(starting_position) unless
      starting_position == 0
  end
end

然后一切都很开心,但我想知道发生了什么。

Rspec测试:

it "assign_campaign assigns a campaign, creates a new campaign task,
    note and deactivates other contact campaigns, one was active previously" do
  # not checking the note portion right now

  contact = FactoryGirl.create(:contact, user: user)
  campaign_to_be_assigned = FactoryGirl.create(:campaign)
  campaign_to_be_assigned.messages << FactoryGirl.create(:message)

  5.times do |i|
    contact.campaigns << FactoryGirl.create(:campaign)
  end
  contact.deactivate_contact_campaigns

  # Randomly activates one of the generated campaigns
  contact.contact_campaigns[rand(4)].activate

  contact.assign_campaign(campaign_to_be_assigned.id)

  contact.contact_campaigns.each do |x|
    if x.campaign_id == campaign_to_be_assigned.id
      x.status.should == 'active'
    else
      x.status.should == 'inactive'
    end
  end
end

正在测试的方法:

def assign_campaign(campaign_id=nil, starting_position = 0,  opts = {})
  if email_is_valid || campaign_id == nil
    if !campaign_id.blank?
      campaign = Campaign.find(campaign_id)
      deactivate_contact_campaigns
      campaigns << campaign if !campaigns.include? campaign
      current_contact_campaign = contact_campaigns.where(campaign_id: campaign_id).first
      current_contact_campaign.activate
      current_contact_campaign.set_current_position(starting_position) unless
        starting_position == 0
      notes.create(user_id: user.id, body: "Assigned #{name} to " +
        campaign.description, group: "system",
        created_at: (opts[:assign_time] ? opts[:assign_time] : Time.now))
      create_new_campaign_task(opts[:user_id],
        (starting_position == 0 ? 0 : (starting_position - 1)))
      return true
    else
      notes.create(user_id: user.id,
        body: "Unassigned #{name} from campaign", group: "system",
        created_at: (opts[:assign_time] ? opts[:assign_time] : Time.now)) if
          contact_campaigns.where(status: 'active').size > 0
      deactivate_contact_campaigns        
    end
  else
    return false
  end
end

测试失败消息

Failures:

  1) Contact assign_campaign assigns a campaign, creates a new campaign task,
      note and deactivates other contact campaigns, one was active previously
     Failure/Error: x.status.should == 'active'
       expected: "active"
            got: nil (using ==)
     # ./spec/models/contact_spec.rb:78:in `block (3 levels) in <top (required)>'
     # ./spec/models/contact_spec.rb:76:in `block (2 levels) in <top (required)>'

FactoryGirl工厂

广告活动

FactoryGirl.define do
  factory :campaign do |campaign|
    sequence(:description) { |n| "description#{n}"}
  end
end

联系人

require 'faker'

FactoryGirl.define do  
  factory :contact do |contact|
    fake_email = Faker::Internet.email
    association :organization, factory: :organization
    sequence(:email) { |n| "#{n}#{fake_email}" }
  end
end

消息

FactoryGirl.define do
  factory :message do |message|
      message.description { "Considering buying your first home?" }
      message.position { 0 }
      message.interval { 0 }
      message.email_subject { "Considering buying your first home?" }
      message.email_body { "I have several valuable resources if you are considering buying your first home. I'm happy to discuss the process of buying a home in simple, easy-to-understand terms, or I can help point you in the right direction to get the best loan for a home purchase.\n\nIf you're just curious what you can get for your money, I'm happy to show you a few properties in your area so you can see what is available. Give me a call today anytime. I look forward to hearing from you." }
  end
end

1 个答案:

答案 0 :(得分:1)

关于您关于object_ids的第一个问题:

object_id是Ruby中分配给实际实例的内部引用ID。没有两个实例可以具有相同的object_id。这与在Rails中调用.id不同,后者应返回数据存储区中记录的id。

为什么您的测试可能失败: 你打电话的那一刻:

contact.contact_campaigns[rand(4)].activate

Rails命中您的数据存储区并在本地缓存该集合。然后你“激活”项目rand(4),这显然是你的意图。

然后你称之为:

contact.assign_campaign(campaign_to_be_assigned.id)

应该已经停用了之前活动的rand(4)。但是,在contact.contact_campaigns的内存中的 NOT 数据存储区中。所以请记住contact.contact_campaigns仍在内存中并认为rand(4)已激活。所以在你这样做之前:

contact.contact_campaigns.each do...

您应该重新加载该集合:

contact.contact_campaigns.reload

希望这有帮助!