2

对 ruby​​、rails 和单元测试来说非常新,所以欢迎任何额外的反馈!

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

测试失败,因为它声称已分配活动的状态为 nil 且不是“活动”(或“非活动”)。

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

例如

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

得到我:

<ContactCampaign id:6,contact_id:1,campaign_id:1,状态: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,状态:“活动”,created_at:“2013-09-11 23:40:07”,updated_at:“2013-09-11 23:40:08”,当前位置: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.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
4

1 回答 1

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 的内存中。所以记住contact.contact_campaigns 还在内存中并且认为rand(4) 被激活了。所以在你这样做之前:

contact.contact_campaigns.each do...

您应该重新加载集合:

contact.contact_campaigns.reload

希望这可以帮助!

于 2013-09-12T04:13:30.623 回答