Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions lib/faker_maker/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ def assert_only_known_attributes_for_override( attr_override_values )

def assert_only_known_and_optional_attributes_for_chaos( chaos_attr_values )
chaos_attr_values = chaos_attr_values.map(&:to_sym)
unknown_attrs = chaos_attr_values - attribute_names
unknown_attrs = chaos_attr_values - attribute_names.flat_map do |item|
item.is_a?(Hash) ? item.keys : item
end
issue = "Can't build an instance of '#{class_name}' " \
"setting '#{unknown_attrs.join( ', ' )}', no such attribute(s)"
raise FakerMaker::NoSuchAttributeError, issue unless unknown_attrs.empty?
Expand Down Expand Up @@ -280,12 +282,15 @@ def manufacture_from_embedded_factory( attr, attributes = {}, chaos: false )
attributes = attr
.embedded_factories
.reject { |e| e == embedded_factory }
.flat_map { |f| pp f.attributes.map(&:name) }
.flat_map { |f| f.attributes(include_embeddings: false).map(&:name) }
.then { |excl| attributes.delete_if { |k, _v| excl.include?(k) } }

# The object that is being manufactured by the factory.
# If an embedded factory name is provided, it builds the object using FakerMaker.
embedded_factory&.build(attributes:, chaos:)
# Chaos is converted to a boolean so that child factories inherit random chaos
# behaviour without receiving attribute names meant for the parent.
embedded_chaos = chaos.is_a?(Array) || chaos.is_a?(String) || chaos.is_a?(Symbol) ? true : chaos
embedded_factory&.build(attributes:, chaos: embedded_chaos)
end

def instantiate
Expand Down
2 changes: 1 addition & 1 deletion lib/faker_maker/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module FakerMaker
VERSION = '5.0.0'
VERSION = '5.0.1'
end
75 changes: 75 additions & 0 deletions spec/faker_maker/factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,81 @@
end
end

describe 'embedded factories' do
it 'builds a factory with multiple embedded factories' do
first_embed = FakerMaker::Factory.new( :first_embed )
first_embed.attach_attribute( FakerMaker::Attribute.new( :colour, proc { 'red' } ) )
FakerMaker.register_factory( first_embed )

second_embed = FakerMaker::Factory.new( :second_embed )
second_embed.attach_attribute( FakerMaker::Attribute.new( :size, proc { 'large' } ) )
FakerMaker.register_factory( second_embed )

factory = FakerMaker::Factory.new( :multi_embed )
factory.attach_attribute( FakerMaker::Attribute.new( :name, proc { 'test' }, required: true ) )
factory.attach_attribute( FakerMaker::Attribute.new( :variant, nil, factory: %i[first_embed second_embed] ) )
FakerMaker.register_factory( factory )

expect { factory.build }.not_to raise_error

fake = factory.build
variant = fake.variant
expect( variant ).to satisfy { |v| v.respond_to?(:colour) || v.respond_to?(:size) }
end

it 'builds with chaos mode enabled on a factory with embedded factories' do
embed = FakerMaker::Factory.new( :chaos_embed )
embed.attach_attribute( FakerMaker::Attribute.new( :value, proc { 'embedded' } ) )
FakerMaker.register_factory( embed )

factory = FakerMaker::Factory.new( :chaos_parent )
factory.attach_attribute( FakerMaker::Attribute.new( :title, proc { 'hello' }, required: true ) )
factory.attach_attribute( FakerMaker::Attribute.new( :child, nil, factory: :chaos_embed ) )
factory.attach_attribute( FakerMaker::Attribute.new( :optional_field, proc { 'maybe' } ) )
FakerMaker.register_factory( factory )

expect { factory.build( chaos: true ) }.not_to raise_error
end

it 'allows chaos mode to target an embedded factory attribute' do
embed = FakerMaker::Factory.new( :target_embed )
embed.attach_attribute( FakerMaker::Attribute.new( :value, proc { 'embedded' } ) )
FakerMaker.register_factory( embed )

factory = FakerMaker::Factory.new( :target_parent )
factory.attach_attribute( FakerMaker::Attribute.new( :title, proc { 'hello' }, required: true ) )
factory.attach_attribute( FakerMaker::Attribute.new( :child, nil, factory: :target_embed ) )
factory.attach_attribute( FakerMaker::Attribute.new( :optional_field, proc { 'maybe' } ) )
FakerMaker.register_factory( factory )

expect { factory.build( chaos: [:child] ) }.not_to raise_error

fakes = []
10.times { fakes << factory.build( chaos: [:child] ) }

fakes.each { |fake| expect( fake.title ).to eq 'hello' }
fakes.each { |fake| expect( fake.optional_field ).to eq 'maybe' }
expect( fakes.map(&:child) ).to include nil
end

it 'does not pass parent chaos attribute names to child factories' do
embed = FakerMaker::Factory.new( :leak_embed )
embed.attach_attribute( FakerMaker::Attribute.new( :inner_value, proc { 'inner' }, required: true ) )
FakerMaker.register_factory( embed )

factory = FakerMaker::Factory.new( :leak_parent )
factory.attach_attribute( FakerMaker::Attribute.new( :name, proc { 'parent' }, required: true ) )
factory.attach_attribute( FakerMaker::Attribute.new( :child, nil, factory: :leak_embed ) )
factory.attach_attribute( FakerMaker::Attribute.new( :optional_thing, proc { 'optional' } ) )
FakerMaker.register_factory( factory )

expect { factory.build( chaos: [:optional_thing] ) }.not_to raise_error

fake = factory.build( chaos: [:optional_thing] )
expect( fake.child.inner_value ).to eq 'inner'
end
end

describe '#instance' do
it 'returns the instance' do
factory = FakerMaker::Factory.new( :factory )
Expand Down
Loading