Rspec: Factory Girl & Complex Associations

716 views Asked by At

I am trying to test a helper method that would return the amount of Subscribers a Campaign is targeting. Being fairly new to TDD, I am having a hard time wrapping my head around how to write this one since the associations are quite complex. A Campaign "targets" by whether a subscriber is on the targeted plan and opted-in to the targeted program.

How do I get my Factory Girl objects to be associated with one another in order to continue writing the spec? As simple as it may seem, I can't get them to cooperate.

Here are the model associations..

models/campaign.rb

class Campaign < ActiveRecord::Base
    belongs_to :program
    belongs_to :plan
end

models/subscriber.rb

class Subscriber < ActiveRecord::Base
    has_many :plans, through: :plannables
end

models/plan.rb

class Plan < ActiveRecord::Base
    has_many :subscribers, through: :plannables
    has_many :plannables
    has_many :campaigns

    validates :name, presence: true,
                uniqueness: true
end

models/program.rb

class Program < ActiveRecord::Base
    has_many :campaigns

    validates :name, presence: true,
                     uniqueness: true
end

models/plannable.rb

class Plannable < ActiveRecord::Base
    belongs_to :plans
    belongs_to :subscribers
end

Here are the Factories..

spec/factories/campaign.rb

FactoryGirl.define do

    factory :campaign do |x|
        x.sequence(:name) { |y| "201#{y} Example" }
        x.sequence(:comment) { |y| "The #{y} example" }
        channels ["Folder", "Fax"]
    end

    factory :program do
        name "Sims"
        comment "the sims program"
    end

    factory :plan do
        name "PDX"
    end
end

spec/factories/subscribers.rb

FactoryGirl.define do
    sequence(:first_name) { |y| "Sam #{y}" }
    sequence(:last_name) { |y| "Sm#{y}th" }
    sequence(:email) { |y| "email#{y}@sub.com".downcase }

    factory :subscriber do
        first_name
        last_name
        email
        password "secret123"
        home_duid { SecureRandom.uuid }
        admin false
        sims true  #opt-in status
        cups false  #opt-in status
        tire false #opt-in status
    end


end

...and my specs..

spec/helpers/application_helper_spec.rb

require "rails_helper"

RSpec.describe ApplicationHelper, :type => :helper do
    before :all do
        @program = create(:program)
        @subscriber = create(:subscriber, star: true)
        @campaign = create(:campaign, plan_id: @plan, program_id: @program)
        @plan = Plan.new(name: "STX)", subscriber_id: @subscriber)
    end

    describe "Campaign subscriber listing" do
        it "finds subscribers that opt-in to affiliated program" do
          expect(@campaign.plan_id).to eq @plan
        end
    end
end

Just as a starter, to simply test an association I created above via object ids, I added that first spec - yet I can't even get that to pass.. Any idea what I might be missing?

Failures:

 1) ApplicationHelper Campaign subscriber listing finds subscribers that opt-in to affiliated program
     Failure/Error: expect(campaign.plan).to eq plan

       expected: #<Plan id: 3, name: "PDX", created_at: "2015-06-16 20:51:18", updated_at: "2015-06-16 20:51:18", plan_id: nil>
            got: nil

       (compared using ==)
     # ./spec/helpers/application_helper_spec.rb:19:in `block (3 levels) in <top (required)>'

UPDATE

I tried using Factory Girl association in my campaign factory..

factory :campaign do |x|
    x.sequence(:name) { |y| "201#{y} Example" }
    x.sequence(:comment) { |y| "The #{y} example" }
    channels ["Folder", "Fax"]
    association plan
end

But receive a confusing error...

* plan - Validation failed: Name has already been taken

I tried changing the name to something random but continuing getting this error.

2

There are 2 answers

0
davidwessman On BEST ANSWER
campaign = create(:campaign, plan_id: @plan, program_id: @program)

Try changing to:

subject(:campaign) { create(:campaign, plan: plan, program: program) }

Would also work to do it with id's, as long as you are consequent.

I made a typo here:

subject(:campaign) { create(:campaign, plan_id: plan_id, program_id: program_id) }

Should be:

subject(:campaign) { create(:campaign, plan_id: plan.id, program:id: program.id) }
1
Andy Waite On

You haven't shown how your test it failing, which makes it a little difficult to help, but you're comparing a plan_id against a @plan object. Try change @campaign.plan_id to @campaign.plan.