Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

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]]
like image 873
Bruno Dias Avatar asked Feb 23 '23 08:02

Bruno Dias


2 Answers

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.

like image 113
jkronz Avatar answered Feb 24 '23 20:02

jkronz


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

like image 44
apneadiving Avatar answered Feb 24 '23 22:02

apneadiving