Rspec testing of counter_cache column's returning 0
Asked Answered
D

2

7

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.

Dexterdexterity answered 29/8, 2012 at 4:47 Comment(0)
I
30

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
Irishirishism answered 29/8, 2012 at 5:16 Comment(6)
Thanks heaps! I wasnt aware that the model was loaded into memory and that a reload was therefore required.Dexterdexterity
ok, so now when doing this with the user.reload method work. But I then wanted to clean up the spec and use the cleaner example you provided, and this isnt working?Dexterdexterity
1) Experimental Data validations comments_count field should have a counter cache Failure/Error: expect { result should have been changed by 1, but was changed by 0 # ./spec/models/experimental_spec.rb:39:in `block (4 levels) in <top (required)>'Dexterdexterity
Sorry void those last couple of comments... This time it was actually a bug in my code! Yay its working!! Thanks heaps! :)Dexterdexterity
I know this is old, and I've probably been looking at this for so long now I'm missing something simple, but any idea why you got that result should have been changed by 1, but was changed by 0 fail? I'm getting the same. I have a situation with soft deletion where I'm manually messing about with counter caches and need to test it in a similar fashion.Apartheid
If you use let!() to set up scenarios, then have a bunch of examples that depend on counter_cache, consider using before { user.reload } to update them all. But keep in mind that may undermine attempts to use .to .changeIndic
C
0

For testng counter_cache you can use Shoulda-Matchers gem, there is method out of the box

Cyano answered 9/9, 2020 at 7:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.