From my understanding, the return value from a factory's 'to_create' method is ignored. This means that the object returned from the 'build' or 'initialize_with' portion of the factory is the object ultimately returned when calling 'create' within a test.
In my case, I am using a variant of the Repository Pattern. I am overriding the 'to_create' portion of the factory to include a call to a repository 'save' method. This method does not modify the given object, but returns an object representing the persisted form of the original.
However, the instance returned from the 'build' block is returned from the factory, and not the instance created in the 'to_create' block. In my code, this means the "unpersisted" form of the object is returned, not the object with updated attributes (e.g. 'id') from the saving action.
Is there a way of forcing the return value of 'create' to be either the result of the 'to_create' block or some value generated within that block?
class Foo
attr_accessor :id, :name
...
end
class FooRepository
def self.create(name)
Foo.new(name) # this object is not yet persisted and has no .id
end
def self.save(foo)
# this method must not guarantee that the original Foo instance
# will always be returned
...
updated_foo # this is a duplicate of the original object
end
...
end
FactoryGirl.define do
factory :foo, class: FooRepository do
# create an example Foo
initialize_with { FooRepository.create(name: "Example") }
# save the Foo to the datastore, returning what may be a duplicate
to_create {|instance| FooRepository.save(instance)}
end
end
describe FooRepository do
it "saves the given Foo to the datastore" do
foo = create(:foo)
foo.id #=> nil
...
end
end
I don't have an answer for you beyond "raise an issue", sorry.
The default to_create callback looks like this:
$ grep to_create lib/factory_girl/configuration.rb
to_create {|instance| instance.save! }
The main problem is that ActiveRecord modifies itself in place when you call save!
on it. FactoryGirl will ignore any new objects that are returned from to_create
.
A quick hack if you want to override the default create strategy:
module FactoryGirl
module Strategy
class Create
def association(runner)
runner.run
end
def result(evaluation)
evaluation.object.tap do |instance|
evaluation.notify(:after_build, instance)
evaluation.notify(:before_create, instance)
instance = evaluation.create(instance) # <-- HACK
evaluation.notify(:after_create, instance)
end
end
end
end
end
... Or do this to your to_create
hook to mimic Rails' in-place modification:
to_create do |record|
new_record = YourRepositoryHere.new.create(record)
record.attributes = new_record.attributes # For example
new_record # Return the record just in case the bug is fixed
end
Best of luck. :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With