Rails 3 and Rspec: counter cache column being updated to 2 when expected 1

☆樱花仙子☆ 提交于 2019-12-07 07:21:11


I'm testing with Rspec a model named Solutions which has many Likes. Solution stores how many Likes it have (counter_cache). It has a "likes_count" attribute (and respective db field).

When I create a Like record associated to a Solution, I expect that the solution attribute "likes_count" should be updated from nil to 1. When I do that in console, it works.

But when I run the spec, doing the SAME THING I do in console, it update TWICE the "likes_count" field, setting it to 2.

Take a look (in console) WORKING:

irb(main):001:0> solution = Factory(:solution)
irb(main):004:0> solution.likes_count 
=> nil
irb(main):006:0> like = Factory(:like, :likeable => solution)
=> #<Like id: 1, user_id: 2, likeable_id: 1, likeable_type: "Solution", 
   created_at: "2011-11-23 19:31:23", updated_at: "2011-11-23 19:31:23">
irb(main):007:0> solution.reload.likes_count
=> 1

Take a look at the spec result NOT WORKING:

 1) Solution counter cache should be increased when a like is created
 Failure/Error: subject.reload.likes_count.should be 1

   expected #<Fixnum:3> => 1
        got #<Fixnum:5> => 2

   Compared using equal?, which compares object identity,
   but expected and actual are not the same object. Use
   'actual.should == expected' if you don't care about
   object identity in this example.
 # ./spec/models/solution_spec.rb:45:in `block (3 levels) in <top (required)>'

Here is the spec:

describe "counter cache" do
  let(:solution) { Factory(:solution) }

  it "should be increased when a like is created" do
    Factory(:like, :likeable => solution)
    solution.reload.likes_count.should be 1

I took a look at test.log and I realized that the db query that updates the counter cache column was called two times in the test.

  SQL (0.5ms)  INSERT INTO "likes" ("created_at", "likeable_id", "likeable_type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?)  [["created_at", Wed, 23 Nov 2011 19:38:31 UTC +00:00], ["likeable_id", 121], ["likeable_type", "Solution"], ["updated_at", Wed, 23 Nov 2011 19:38:31 UTC +00:00], ["user_id", 204]]
  SQL (0.3ms)  UPDATE "solutions" SET "likes_count" = COALESCE("likes_count", 0) + 1 WHERE "solutions"."id" IN (SELECT "solutions"."id" FROM "solutions" WHERE "solutions"."id" = 121 ORDER BY id DESC)
  SQL (0.1ms)  UPDATE "solutions" SET "likes_count" = COALESCE("likes_count", 0) + 1 WHERE "solutions"."id" IN (SELECT "solutions"."id" FROM "solutions" WHERE "solutions"."id" = 121 ORDER BY id DESC)
  Solution Load (0.3ms)  SELECT "solutions".* FROM "solutions" WHERE "solutions"."id" = ? LIMIT 1  [["id", 121]]


I had the same problem. It turned out that my spec_helper.rb was loading the models a second time and therefore creating a second callback to update the counters. Make sure your Solution model isn't being reloaded by another process.

The answer above is also correct: you need to use == instead of be to do the comparison, but that will not fix the multiple updates that you are seeing in your log file.


You've the answer in your logs:

  • When you use be, it compares the object_id which is always the same for a couple of objects like true and 1. The id of 1 appears to be 2. Try in console: 1.object_id #=> 2

  • So replace your test with: solution.reload.likes_count.should eql 1 or even solution.reload.likes_count.should == 1