stephenmurdoch
stephenmurdoch

Reputation: 34633

Rspec giving false negative whilst testing pagination

I'm writing a request spec, to test that will_paginate is working ok, and I've got a few problems. Firstly, here's a pruned version of my spec:

require 'spec_helper'

describe "Articles" do

  subject { page }

  describe "index page" do

    let(:user) { FactoryGirl.create(:user) }

    before { visit news_path }

    describe "pagination" do

      before(:all) { 31.times { FactoryGirl.create(:article, user: user) } }
      after(:all) { Article.delete_all; User.delete_all }

      let(:first_page)  { Article.paginate(page: 1) }
      let(:second_page) { Article.paginate(page: 2) }

      it "should not list the second page of articles" do
        second_page.each do |article|
          page.should_not have_selector('li', text: article.title)
        end
      end
    end
  end
end

As you can see there is a test to ensure that the second page of articles are not shown when the user visits the articles index page. This test fails:

1) Articles index page pagination should not list the second page of articles
   Failure/Error: page.should_not have_selector('li', text: article.title)
   expected css "li" with text "Article number 1" not to return anything

I can't understand why this is failing. When I manually create 31 articles, in development env, and view it in the browser, pagination works fine, but when I switch to the test env, the specs fail.

Article Model:

class Article < ActiveRecord::Base
  attr_accessible :body, :title
  belongs_to :user

  validates :user_id, presence: true

  default_scope order: 'created_at DESC'
end

Article Factory looks like this:

FactoryGirl.define do
  factory :article do
    sequence(:title)  { |n| "Article number #{n}" }
    body "This is the body"
    user
  end
end

Upvotes: 2

Views: 720

Answers (1)

stephenmurdoch
stephenmurdoch

Reputation: 34633

Quite incredibly, the solution to this was to do the following;

Change:

before(:all) { 31.times { FactoryGirl.create(:article, user: user) } }

to:

before do
  31.times { FactoryGirl.create(:article, user: user) }
  visit news_path
end

Two things I learned here:

  1. The before block must not target (:all), otherwise tests fail
  2. I need to explicitly run visit news_path inside the before block, after the creation of the factories, otherwise capybara's page object will not be what I expect it to be

So, to illustrate:

This won't work:

# fails because it targets (:all)
before(:all) do
  31.times { FactoryGirl.create(:article, user: user) }
  visit news_path
end

And neither will this:

# fails because we are visiting the news path before the objects exist
before do
  visit news_path
  31.times { FactoryGirl.create(:article, user: user) }
end

It needs to be this:

# not targeting (:all) and only visiting news path after creation of the objects
before do
  31.times { FactoryGirl.create(:article, user: user) }
  visit news_path
end

Over 20 hours to figure this out, at least I learned some new stuff etc.

Upvotes: 4

Related Questions