Be careful when stubbing out attributes on records that are defined by associations. Nothing is as it seems to be.
The associated record has its own universe of things; when delegating calls to it, you ca not stub methods on the associated record and expect them to be around. That is a general issue with this pattern/approach.
What's happening?
Consider these classes:
class Post < ActiveRecord::Base
belongs_to :thread
def thread_title
thread.title
end
end
class Thread < ActiveRecord::Base
has_many :posts
end
Now look at this spec:
thread = Thread.make
post = Post.make :thread => thread
thread.stub :title => 'Hello Universe'
post.thread_title.should == 'Hello Universe'
Unfortunately, this spec will fail as post.thread_title
is nil.
Why? Because post.thread
is not actually a Thread
even though looking at its class
makes you believe so:
post.thread.class
=> Thread
Its real class is ActiveRecord::Associations::BelongsToAssociation
which is sometimes exposed, for example when calling undefined methods:
post.thread.foobar
NoMethodError Exception: undefined method `foobar' for #<ActiveRecord::Associations::BelongsToAssociation:0xc7d99a0>
This means that you actually stubbed on the BelongsToAssociation
object -- those stubs will be gone when our post
accesses its thread
.\
That also applies when you stub on post.thread
instead of thread
in the spec; they both have the same object_id
.
When debugging, you will also see that post.thread.title
is in fact "Hello Universe" while post.thread_title
is nil.
How to fix it
Both these approaches work and you are probably doing one of them for most of your specs anyway:
-
Stub the method that uses the associated record.
-
Stub the association explicitly:
post.stub :thread => thread
You need to do this even though (or rather: because of) the belongs_to association that is already in place.