I have some nested models that require a bit more than the standard accepts_nested_attributes_for
logic. Instead of automatically creating or updating child records based on id key, in this case the child records must already exist and have certain other conditions, or it raises an error.
So as part of this, I have a parent model iterating through the given children attributes and calling #update_attributes on the child records that match.
My question is how to mock the #update_attributes
in the specs for the parent model. I want to test that the child record(s) identified by the given attributes are in fact the one(s) that receives #update_attributes
My first approach was to try mock_model, something like this (not using the actual model names but simply 'subject' and 'children' for clarity):
before :each do
subject.children = mock_model(Child), mock_model(Child), mock_model(Child)
subject.save!
@expected = subject.children[1]
@input = {:child_id => @expected.id, :value => 'something'}
end
it 'should call #update_attributes on the child with given attributes' do
@expected.should_receive(:update_attributes).
with({:value => 'something'})
subject.merge_children_attributes(@input)
end
However, you can't actually persist mock models. And you can't use stub models either for the same reason. And I need persisted records since I use children.find()
or children.all()
in the implementation. (unless I mocked the children
association, which doesn't seem right given it's a method of the subject).
So then I went to fixtures.
fixtures :children
before :each do
subject.children = children(:one), children(:two), children(:three)
subject.save!
@expected = subject.children[1]
@input = {:child_id => @expected.id, :value => 'something'}
end
it 'should call #update_attributes on the child with given attributes' do
@expected.should_receive(:update_attributes).
with({:value => 'something'})
subject.merge_children_attributes(@input)
end
The problem here is that the expected matched record (@expected
) never receives :update_attributes
-- with anything.
With much puts
ing around, the reason for this is that when the implementation calls children.find()
, it returns a different instance than the @expected = subject.children[1]
in the spec. Although the :id attribute is the same. ActiveRecord is no identity map...
So how would I write this spec to pass?
UPDATE
I confirmed looking at the log that the correct record is in fact being updated. So it's frustrating that I can't seem to mock :update_attributes
because of object identity not being maintained between find
s.