rico_mac
rico_mac

Reputation: 888

testing relationships in rspec, rails

I am trying to write a test to check that an associated object exists and the relevant id is stored in the database.

A panel belongs to a page. A page has many panels.

A sample of the current errors I have is this

CorporatePanel 
     Failure/Error: @instance = corporate_panel
     ActiveRecord::RecordInvalid:
       Validation failed: Corporate page does not exist, Corporate page can't be blank
     # ./spec/support/project_helpers.rb:11:in `block (2 levels) in setup_factories'
     # ./spec/models/corporate_panel_spec.rb:8:in `block (2 levels) in <top (required)>'
     # -e:1:in `<main>'

Which I believe is caused because my test isnt creating an instance of page before a panel is created.

In my panel model I use this

  validate :check_associates

  private

  def check_associates
    associated_object_exists CorporatePage, :corporate_page_id
  end

The method 'associated_model_exists' is a helper

  def associated_object_exists(klass,fn,required=true)
    id=self.send(fn)
    id_setter=(fn.to_s+'=').to_sym
    unless klass.find_by_id(id)
      self.send(id_setter,nil)
      id=nil
    end
    if required==true and id.nil?
      errors.add(fn,"does not exist")
      return false
    end
  end

The Panel test looks like this,

    setup_factories

  before do
    @instance = corporate_panel
  end

  it { should belong_to(:corporate_page) }

  mandatory_integer :section
  mandatory_belongs_to CorporatePage

The madatory belongs_to looks like this

  def mandatory_belongs_to(model)
    context "#{model}" do
      context "where the id is incorrect" do
        before do
          allow(model).to receive(:find_by_id).and_return(nil)
        end
        it "should be invalid" do
          expect(@instance).to_not be_valid
        end
      end
      context "where the id is correct" do
        before do
          allow(model).to receive(:find_by_id).and_return(true)
        end
        it "should be valid" do
          expect(@instance).to be_valid
        end
      end
    end
  end

Update

FactoryGirl.define do
  factory :corporate_panel do
    corporate_page_id 1
    section 1
    # position 1
    panel_type "MyString"
    title "MyString"
    headline "MyString"
    body "MyText"
    workflow_state "MyString"
  end
end

FactoryGirl.define do
  factory :corporate_page do
    title "MyString"
    static_descriptor "Home"
    # workflow_state "MyString"
  end
end

How would I ensure that a page is created before the model, if that is even the problem?

Upvotes: 0

Views: 290

Answers (2)

Surya
Surya

Reputation: 15992

The problem is the way you've defined your factory. It should create the appropriate association while creating the corporate panel. It should be something like this:

FactoryGirl.define do
  factory :corporate_page do
    title "MyString"
    static_descriptor "Home"
    # workflow_state "MyString"
  end
end


FactoryGirl.define do
  factory :corporate_panel do
    corporate_page # this refers to the factory defined above!! and will create appropriate association.
    section 1
    # position 1
    panel_type "MyString"
    title "MyString"
    headline "MyString"
    body "MyText"
    workflow_state "MyString"
  end
end

Here are some links for further reading on factories: http://www.hiringthing.com/2012/08/17/rails-testing-factory-girl.html and https://github.com/brennovich/cheat-ruby-sheets/blob/master/factory_bot.md

Upvotes: 1

zetetic
zetetic

Reputation: 47548

You can use an association as recommended in Surya's answer. If you need more control over the creation of factories, you can also explicitly pass in the Corporate page when creating the CorporatePanel:

  let(:corporate_page) { FactoryGirl.create :corporate_page }
  let(:corporate_panel) { FactoryGirl.build :corporate_panel, corporate_page: corporate_page }

This might be preferable if you want to use the :corporate_panel factory in other examples without an associated corporate_page.

Upvotes: 0

Related Questions