describe "initialize_with with non-FG attributes" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string, age: :integer) do def self.construct(name, age) new(name: name, age: age) end end FactoryBot.define do factory :user do initialize_with { User.construct("John Doe", 21) } end end end subject { build(:user) } its(:name) { should eq "John Doe" } its(:age) { should eq 21 } end describe "initialize_with with FG attributes that are transient" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string) do def self.construct(name) new(name: "#{name} from .construct") end end FactoryBot.define do factory :user do transient do name { "Handsome Chap" } end initialize_with { User.construct(name) } end end end subject { build(:user) } its(:name) { should eq "Handsome Chap from .construct" } end describe "initialize_with non-ORM-backed objects" do include FactoryBot::Syntax::Methods before do define_class("ReportGenerator") do attr_reader :name, :data def initialize(name, data) @name = name @data = data end end FactoryBot.define do sequence(:random_data) { Array.new(5) { Kernel.rand(200) } } factory :report_generator do transient do name { "My Awesome Report" } end initialize_with { ReportGenerator.new(name, FactoryBot.generate(:random_data)) } end end end it "allows for overrides" do expect(build(:report_generator, name: "Overridden").name).to eq "Overridden" end it "generates random data" do expect(build(:report_generator).data.length).to eq 5 end end describe "initialize_with parent and child factories" do before do define_class("Awesome") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :awesome do transient do name { "Great" } end initialize_with { Awesome.new(name) } factory :sub_awesome do transient do name { "Sub" } end end factory :super_awesome do initialize_with { Awesome.new("Super") } end end end end it "uses the parent's constructor when the child factory doesn't assign it" do expect(FactoryBot.build(:sub_awesome).name).to eq "Sub" end it "allows child factories to override initialize_with" do expect(FactoryBot.build(:super_awesome).name).to eq "Super" end end describe "initialize_with implicit constructor" do before do define_class("Awesome") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :awesome do transient do name { "Great" } end initialize_with { new(name) } end end end it "instantiates the correct object" do expect(FactoryBot.build(:awesome, name: "Awesome name").name).to eq "Awesome name" end end describe "initialize_with doesn't duplicate assignment on attributes accessed from initialize_with" do before do define_class("User") do attr_reader :name attr_accessor :email def initialize(name) @name = name end end FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :user do email name { email.gsub(/@.+/, "") } initialize_with { new(name) } end end end it "instantiates the correct object" do built_user = FactoryBot.build(:user) expect(built_user.name).to eq "person1" expect(built_user.email).to eq "person1@example.com" end end describe "initialize_with has access to all attributes for construction" do it "assigns attributes correctly" do define_class("User") do attr_reader :name, :email, :ignored def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] @ignored = attributes[:ignored] end end FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :user do transient do ignored { "of course!" } end email name { email.gsub(/@.+/, "") } initialize_with { new(**attributes) } end end user_with_attributes = FactoryBot.build(:user) expect(user_with_attributes.email).to eq "person1@example.com" expect(user_with_attributes.name).to eq "person1" expect(user_with_attributes.ignored).to be_nil end end describe "initialize_with with an 'attributes' attribute" do it "assigns attributes correctly" do define_class("User") do attr_reader :name def initialize(attributes:) @name = attributes[:name] end end FactoryBot.define do factory :user do attributes { {name: "Daniel"} } initialize_with { new(**attributes) } end end user = FactoryBot.build(:user) expect(user.name).to eq("Daniel") end end describe "initialize_with for a constructor that requires a block" do it "executes the block correctly" do define_class("Awesome") do attr_reader :output def initialize(&block) @output = instance_exec(&block) end end FactoryBot.define do factory :awesome do initialize_with { new { "Output" } } end end expect(FactoryBot.build(:awesome).output).to eq "Output" end end describe "initialize_with with a hash argument" do it "builds the object correctly" do define_class("Container") do attr_reader :contents def initialize(contents) @contents = contents end end FactoryBot.define do factory :container do initialize_with { new({key: :value}) } end end expect(FactoryBot.build(:container).contents).to eq({key: :value}) end end