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