ArashM
ArashM

Reputation: 1409

Testing page view counter with rspec

I've added a very simple counter to my post model. Whenever someone open a post, it updates the corresponding field :counter by 1.

  def show
    @story = Story.find(params[:id])
    @comment = @story.comments.build
    @comments = @story.comments.arrange(order: :created_at)

    @story.increment!(:counter) # ---Here---

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @story }
    end
  end

Actually, I checked the DB and this thing is working. But my test get fail:

it "raise the counter by one when visiting a page" do
  story = FactoryGirl.create(:story)
  expect {
    visit story_url(story)
    }.to change { story.counter }.by(1)
end

And it shows:

1) Stories raise the counter by one when visiting a page
  Failure/Error: expect {
    result should have been changed by 1, but was changed by 0
    # ./spec/requests/stories_spec.rb:8:in `block (2 levels) in <top (required) >'

here is my test.log:

Started POST "/stories" for 127.0.0.1 at 2012-09-22 11:31:32 +0330
Processing by StoriesController#create as HTML
  Parameters: {"utf8"=>"✓", "story"=>{"title"=>"sfomqd8s2r", "content"=>"Beatae voluptatibus quia cumque voluptate. Beatae sed aut sit. Fugiat deleniti et vitae accusantium blanditiis. Consequatur at itaque temporibus autem. Quo beatae delectus minus porro et.", "tag_names"=>""}, "commit"=>"submit"}
   (0.1ms)  SELECT MIN("tags"."stories_count") AS min_id FROM "tags" 
   (0.1ms)  SELECT MAX("tags"."stories_count") AS max_id FROM "tags" 
  Tag Load (0.1ms)  SELECT name, stories_count FROM "tags" ORDER BY name asc
   (0.1ms)  SAVEPOINT active_record_1
  SQL (1.5ms)  INSERT INTO "stories" ("comments_count", "content", "counter", "created_at", "publish_date", "slug", "title", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)  [["comments_count", 0], ["content", "Beatae voluptatibus quia cumque voluptate. Beatae sed aut sit. Fugiat deleniti et vitae accusantium blanditiis. Consequatur at itaque temporibus autem. Quo beatae delectus minus porro et."], ["counter", 0], ["created_at", Sat, 22 Sep 2012 11:31:32 IRST +03:30], ["publish_date", nil], ["slug", nil], ["title", "sfomqd8s2r"], ["updated_at", Sat, 22 Sep 2012 11:31:32 IRST +03:30], ["user_id", nil]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
Redirected to http://www.example.com/stories/2
Completed 302 Found in 18ms (ActiveRecord: 1.9ms)


Started GET "/stories/2" for 127.0.0.1 at 2012-09-22 11:31:32 +0330
Processing by StoriesController#show as HTML
  Parameters: {"id"=>"2"}
   (0.1ms)  SELECT MIN("tags"."stories_count") AS min_id FROM "tags" 
   (0.1ms)  SELECT MAX("tags"."stories_count") AS max_id FROM "tags" 
  Tag Load (0.1ms)  SELECT name, stories_count FROM "tags" ORDER BY name asc
  Story Load (0.1ms)  SELECT "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT 1  [["id", "2"]]
  CACHE (0.0ms)  SELECT "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT 1  [["id", "2"]]
  Comment Load (0.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."story_id" = 2 ORDER BY (case when comments.ancestry is null then 0 else 1 end), comments.ancestry, created_at
  SQL (0.3ms)  UPDATE "stories" SET "counter" = 1 WHERE "stories"."id" = 2
  Tag Load (0.2ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."story_id" = 2
  Rendered stories/_story_operation.html.erb (3.5ms)
  Rendered stories/_story.html.erb (5.4ms)
  Rendered comments/_form.html.erb (7.1ms)
Completed 200 OK in 34ms (Views: 18.1ms | ActiveRecord: 1.1ms | Solr: 0.0ms)
   (0.7ms)  rollback transaction

I can't understand whats happening. Maybe it's something about Capybara or the way I've wrote the test.

Upvotes: 2

Views: 349

Answers (1)

Frederick Cheung
Frederick Cheung

Reputation: 84142

As you know,

expect {
  visit story_url(story)
}.to change { story.counter }.by(1)

compares the value of story.counter before and after the block is executed. When your controller loads and modifies the story, that is a completely separate ruby object (that happens to refer to the same row in the database): the story object in your spec code is unaware of the changes made to the database and holds the same (now stale) data.

To get your spec passing you could instead check whether story.reload.counter is changing.

Upvotes: 2

Related Questions