title: rspec testing has_many :through and after_save
body: <p>I have an (I think) relatively straightforward <code>has_many :through</code> relationship with a join table:</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    has_many :user_following_thing_relationships
    has_many :things, :through =&gt; :user_following_thing_relationships
  end
  
  class Thing &lt; ActiveRecord::Base
    has_many :user_following_thing_relationships
    has_many :followers, :through =&gt; :user_following_thing_relationships, :source =&gt; :user
  end
  
  class UserFollowingThingRelationship &lt; ActiveRecord::Base
    belongs_to :thing
    belongs_to :user
  end
  </code></pre>
  
  <p>And these rspec tests (I know these are not necessarily good tests, these are just to illustrate what's happening):</p>
  
  <pre><code>describe Thing do     
    before(:each) do
      @user = User.create!(:name =&gt; "Fred")
      @thing = Thing.create!(:name =&gt; "Foo")    
      @user.things &lt;&lt; @thing
    end
  
    it "should have created a relationship" do
      UserFollowingThingRelationship.first.user.should == @user
      UserFollowingThingRelationship.first.thing.should == @thing
    end
  
    it "should have followers" do
      @thing.followers.should == [@user]
    end     
  end
  </code></pre>
  
  <p>This works fine UNTIL I add an <code>after_save</code> to the <code>Thing</code> model that references its <code>followers</code>.  That is, if I do</p>
  
  <pre><code>class Thing &lt; ActiveRecord::Base
    after_save :do_stuff
    has_many :user_following_thing_relationships
    has_many :followers, :through =&gt; :user_following_thing_relationships, :source =&gt; :user
  
    def do_stuff
      followers.each { |f| puts "I'm followed by #{f.name}" }
    end
  end
  </code></pre>
  
  <p>Then the second test fails - i.e., the relationship is still added to the join table, but <code>@thing.followers</code> returns an empty array.  Furthermore, that part of the callback never gets called (as if <code>followers</code> is empty within the model).  If I add a <code>puts "HI"</code> in the callback before the <code>followers.each</code> line, the "HI" shows up on stdout, so I know the callback is being called.  If I comment out the <code>followers.each</code> line, then the tests pass again.</p>
  
  <p>If I do this all through the console, it works fine.  I.e., I can do</p>
  
  <pre><code>&gt;&gt; t = Thing.create!(:name =&gt; "Foo")
  &gt;&gt; t.followers # []
  &gt;&gt; u = User.create!(:name =&gt; "Bar")
  &gt;&gt; u.things &lt;&lt; t
  &gt;&gt; t.followers  # [u]
  &gt;&gt; t.save    # just to be super duper sure that the callback is triggered
  &gt;&gt; t.followers  # still [u]
  </code></pre>
  
  <p>Why is this failing in rspec?  Am I doing something horribly wrong?</p>
  
  <p><strong>Update</strong></p>
  
  <p>Everything works if I manually define <code>Thing#followers</code> as</p>
  
  <pre><code>def followers
    user_following_thing_relationships.all.map{ |r| r.user }
  end
  </code></pre>
  
  <p>This leads me to believe that perhaps I am defining my <code>has_many :through</code> with <code>:source</code> incorrectly?</p>
  
  <p><strong>Update</strong></p>
  
  <p>I've created a minimal example project and put it on github: <a href="https://github.com/dantswain/RspecHasMany" rel="noreferrer">https://github.com/dantswain/RspecHasMany</a></p>
  
  <p><strong>Another Update</strong></p>
  
  <p>Thanks a ton to @PeterNixey and @kikuchiyo for their suggestions below.  The final answer turned out to be a combination of both answers and I wish I could split credit between them.  I've updated the github project with what I think is the cleanest solution and pushed the changes: <a href="https://github.com/dantswain/RspecHasMany" rel="noreferrer">https://github.com/dantswain/RspecHasMany</a></p>
  
  <p>I would still love it if someone could give me a really solid explanation of what is going on here.  The most troubling bit for me is why, in the initial problem statement, everything (except the operation of the callback itself) would work if I commented out the reference to <code>followers</code>.</p>