Read more

Why stubbing on associated records does not always work as expected

Arne Hartherz
August 17, 2012Software engineer at makandra GmbH

Be careful when stubbing out attributes on records that are defined by associations. Nothing is as it seems to be.

Illustration web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
Read more Show archive.org snapshot

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:

  1. Stub the method that uses the associated record.

  2. 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.

Posted by Arne Hartherz to makandra dev (2012-08-17 16:22)