user1185081
user1185081

Reputation: 2118

how to test a polymorphic association with FactoryBot on Rails 5.2?

I write basic tests to specify validations made at the models level. I use Rspec and FactoryBot. The BusinessObject model can have 2 parents: either BusinessArea, or BusinessProcess.

Extract of the BusinessObject model:

# == Schema Information
#
# Table name: business_objects
#
#  id                 :integer          not null, primary key
#  playground_id      :integer          not null
#  main_scope_id      :integer
#  code               :string(30)       not null
#  name               :string(200)      not null
#  description        :text
#  area_process_type  :string
#  area_process_id    :integer

class BusinessObject < ActiveRecord::Base
  belongs_to :area_process, polymorphic: true
  validates :area_process, presence: true
...
end

Extract of the BusinessArea model:

class BusinessArea < ActiveRecord::Base
  has_many :business_objects, as: :area_process
...
end

Extract of the BusinessProcess model:

class BusinessProcess < ActiveRecord::Base
  has_many :business_objects, as: :area_process
...
end

The Factory:

FactoryBot.define do
  factory :business_object do
    association :area_process,  factory: :business_area
    name                {"Test Business Object"}
    code                {"TEST_BO"}
    description         {"This is a test Business object used for unit testing"}
    created_by          {"Fred"}
    updated_by          {"Fred"}
    owner_id            {1}
    status_id           {0}
    end

end

When running the tests, the Factory fails with the following message:

7) BusinessObject has a valid factory Failure/Error: expect(build(:business_object)).to be_valid

 ActiveRecord::StatementInvalid:
   PG::UndefinedColumn: ERROR:  Column business_objects.business_area_id does not exist.

How to specify to the Factory the parent to use in the association?

Thanks a lot!

Upvotes: 0

Views: 1879

Answers (2)

Bishal Mukherjee
Bishal Mukherjee

Reputation: 51

I found the solution here, https://github.com/thoughtbot/factory_bot/issues/1226

Define the Factory for business_area and business_process. And in business_object factory write something like this

factory :business_object do
  #define attributes
  transient do
    area_process { nil }
  end

  area_process_id { area_process.id }
  area_process_type { area_process.class.name }
end

And in spec

let(:business_area) { create(:business_area) }
let(:business_object) { create(:business_object, area_process: business_area) }

Upvotes: 4

user1185081
user1185081

Reputation: 2118

I found the solution as a sub-factory declaration, thanks to Factory Bot cheatsheet.

I must specify the business object's parent when defining the factory:

FactoryBot.define do
  factory :business_object, parent: :business_area do
    playground_id       {0}
    name                {"Test Business Object"}
    code                {"TEST_BO"}
    description         {"This is a test Business object used for unit testing"}
    created_by          {"Fred"}
    updated_by          {"Fred"}
    owner_id            {1}
    status_id           {0}
  end
end

Then the factory refers to the required parent when creating the tested business object.

EDIT: in some context, this appeared not to be sufficient, so I had to be more explicit:

FactoryBot.define do
  factory :business_object do
    association :parent, factory: :business_area, strategy: :build
    playground_id       {0}
    name                {"Test Business Object"}
    code                {"TEST_BO"}
    description         {"This is a test Business object used for unit testing"}
    created_by          {"Fred"}
    updated_by          {"Fred"}
    owner_id            {1}
    status_id           {0}
  end
end

Upvotes: 1

Related Questions