Reputation: 2118
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
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
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