Rspec testing of counter_cache column's returning 0

counter-cache, rspec, rspec2, ruby, ruby-on-rails

Solution

The `counter_cache` is getting updated in the database directly. This will not affect the copy of the model you have loaded into memory so you need to reload it:

it "media_count should == 1 " do 
  user.reload
  user.media_count.should == 1
end

But, I don't think that is how I would test this. As you have it, your test is very tightly coupled to setup code that seem like it doesn't need to be there at all. How about something like this for a stand alone spec:

it "has a counter cache" do
    user = FactoryGirl.create(:user)
    expect { 
      user.media.create(caption: "Test media") 
    }.to change { User.last.media_count }.by(1)
end

Problem

For days now I have been trying to get to the bottom of what seam to be something that should be very easy to do... I am however still very new to the world of rails and ruby and I just cant work this one out... :p Anyway the problem I am having is that I have a number of :counter_cache columns in my model's, which are all working quite nicely when testing them manually. However I am wanting to do the TDD thing and I cant seam to test them in rspec for some unknown reason?? Anyway here is an example of my model's (User's, comments & Media): ``` class User < ActiveRecord::Base has_many :comments has_many :media, dependent: :destroy end class Comment < ActiveRecord::Base attr_accessible :content, :user_id belongs_to :commentable, polymorphic: true, :counter_cache => true belongs_to :user, :counter_cache => true validates :user_id, :presence => true validates :content, :presence => true, :length => { :maximum => 255 } end class Medium < ActiveRecord::Base attr_accessible :caption, :user_id belongs_to :user, :counter_cache => true has_many :comments, as: :commentable validates :user_id, presence: true validates :caption, length: { maximum: 140 }, allow_nil: true, allow_blank: true default_scope order: 'media.created_at DESC' end ``` Here are sample's of the table's schema setup: ``` create_table "users", :force => true do |t| t.integer "comments_count", :default => 0, :null => false t.integer "media_count", :default => 0, :null => false end create_table "comments", :force => true do |t| t.text "content" t.integer "commentable_id" t.string "commentable_type" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.integer "user_id" end create_table "media", :force => true do |t| t.integer "user_id" t.string "caption" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.integer "comments_count", :default => 0, :null => false end ``` And now here is an sample of an rspec example I have tried: ``` require 'spec_helper' describe "Experimental" do describe "counter_cache" do let!(:user) { FactoryGirl.create(:user)} subject { user } before do @media = user.media.create(caption: "Test media") end its "media array should include the media object" do m = user.media m.each do |e| puts e.caption # Outputting "Test media" as expected end user.media.should include(@media) #This works end it "media_count should == 1 " do # This isnt working? puts user.media_count #Outputting 0 user.media_count.should == 1 end end end ``` And finally the error message that rspec is giving me: ``` Failures: 1) Experimental counter_cache media_count should == 1 Failure/Error: user.media_count.should == 1 expected: 1 got: 0 (using ==) # ./spec/models/experimental_spec.rb:24:in `block (3 levels) in <top (required)>' Finished in 0.20934 seconds 2 examples, 1 failure ``` Also note that this is happening for all my counter_cache column in all my model's. I have also tried a number of different way's of testing this, but they are all returning the above error message. Really hoping someone can help me out with this. :) Thanks heaps in advance! Luke Update: This affects counter_culture the same way, and the solution below also fixes the problem for counter_culture.

Original source