rspec/rspec-rails — Issue #486: stub_model forces a stub on primary_key
Author: petervandenabeele
Created: 2012-01-17 15:20:20+00:00
State: open

For ActiveRecord derived classes, this code:

```
# File 'lib/rspec/rails/mocks.rb', line 181

def stub_model(model_class, stubs={})
model_class.new.tap do |m|
m.extend ActiveModelStubExtensions
if defined?(ActiveRecord) && model_class < ActiveRecord::Base
m.extend ActiveRecordStubExtensions
primary_key = model_class.primary_key.to_sym
stubs = stubs.reverse_merge(primary_key => next_id)
stubs = stubs.reverse_merge(:persisted? => !!stubs[primary_key])
else
stubs = stubs.reverse_merge(:id => next_id)
stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
end
stubs.each do |k,v|
m.__send__("#{k}=", stubs.delete(k)) if m.respond_to?("#{k}=")
end
m.stub(stubs)
yield m if block_given?
end
end
```

always forces some kind of stub on the primary_key.

Even with a `:id => nil` or `stub_model(Person).as_new_record` the primary key is touched (it is set to nil then).

We have a piece of after_initialize code that sets the primary key (uuid) in our case, to an actual UUID.

But I see no way of preserving that after_initialize for stub_model(OurActiveRecordModel).

I wanted to give extend the stub_model with a third parameter (do_not_touch_primary_key), but I am uncertain if that would work, since the second parameter is a hash and that will absorb all the hash entries in the params list, not sure it would continue to work if a third, optional parameter is added.

A second problem, but less fundamental, is that the primary_key is set to a number (1001 e.g.) but the primary_key is a UUID (represented here as a string with 32 digits and 4 '-' characters). In postgresql, the query complains then with

```
ActionView::Template::Error:
PGError: ERROR: operator does not exist: character varying = integer
LINE 1: ...M "addresses" WHERE "addresses"."addressable_id" = 1002 AND...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT "addresses".* FROM "addresses" WHERE "addresses"."addressable_id" = 1002
```

I created a work-around now with `:uuid => UUIDTools::UUID.random_create.to_s` in the list of stubs, but I should not have to write that, since I already have that code in the model after_initialize.