Question

I have my problems with my rspec tests particularly with the has_content method. Here are some of the code snippets. I have been following the Ruby on Rails tutorials from Michael Hartl, in case you don't know.

has_content always returns false even though the microposts have set its respective content.

resulting test

Run options: include {:line_numbers=>[61]}
..FFF

Failures:

1) User pages profile page microposts should have content 0
 Failure/Error: it { should have_content(user.microposts.count) }
   expected #has_content?(0) to return true, got false
 # ./spec/requests/user_pages_spec.rb:72:in `block (4 levels) in <top (required)>'

2) User pages profile page microposts should have content "Foo Bar"
 Failure/Error: it { should have_content(m2.content) }
   expected #has_content?("Foo Bar") to return true, got false
 # ./spec/requests/user_pages_spec.rb:71:in `block (4 levels) in <top (required)>'

3) User pages profile page microposts should have content "Lorem Ipsum"
 Failure/Error: it { should have_content(m1.content) }
   expected #has_content?("Lorem Ipsum") to return true, got false
 # ./spec/requests/user_pages_spec.rb:70:in `block (4 levels) in <top (required)>'

Finished in 0.74407 seconds
5 examples, 3 failures

Failed examples:

rspec ./spec/requests/user_pages_spec.rb:72 # User pages profile page microposts should      have content 0
rspec ./spec/requests/user_pages_spec.rb:71 # User pages profile page microposts should have content "Foo Bar"
rspec ./spec/requests/user_pages_spec.rb:70 # User pages profile page microposts should have content "Lorem Ipsum"

Randomized with seed 43497

[Finished in 1.3s with exit code 1]

user_pages_spec.rb

describe "profile page" do
    let(:user) { FactoryGirl.create(:user) }
    let(:m1) { FactoryGirl.create(:micropost, user: user, content: "Lorem Ipsum") }
    let(:m2) { FactoryGirl.create(:micropost, user: user, content: "Foo Bar") }

    before { visit user_path(user) }

    it { should have_selector('h1', text: user.name) } 
    it { should have_title(full_title(user.name)) }

    describe "microposts" do
        it { should have_content(m1.content) }
        it { should have_content(m2.content) }
        it { should have_content(user.microposts.count) }
    end
end

show.hml.erb

<% provide(:title, @user.name) %>

<div class='row'>
    <aside class='col-md-4'>
        <section>
            <h1>
                <%= gravatar_for @user, size: 50 %>
                <%= @user.name %>
            </h1>
        </section>
    </aside>

    <div class="col-md-8">
        <% if @user.microposts.any? %>
            <h3>Microposts (<%= @user.microposts.count %>)</h3>.
            <ol class="microposts">
                <%= render @microposts %>
            </ol>
        <% end %>
    </div>
</div>

_micropost.html.erb

<li>
    <span class="content">
        <%= micropost.content %>
    </span>
    <span class="timestamp">
        Posted <%= time_ago_in_words(micropost.created_at) %>
    </span>
</li>

micropost.erb

class Micropost < ActiveRecord::Base
    attr_accessible :content
    belongs_to :user

    validates :user_id, presence: true
    validates :content, presence: true, length: { maximum: 140 }

    default_scope { order('microposts.created_at DESC') }
end

user.rb

class User < ActiveRecord::Base
    attr_accessible :name, :email, :password, :password_confirmation
    has_secure_password
    has_many :microposts, dependent: :destroy

    # remaning lines are for validation

end

===EDIT (answer to problem)===

I used let! instead to force the method's invocation before each example.

let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Lorem Ipsum") }
let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Foo Bar") }
Was it helpful?

Solution

let method defines a method with given block, so every time you call user you create a new one, and every time you call m1 or m2 you create new micropost with a new user. Instead do:

before(:each) do
  @user = FactoryGirl.create(:user)
  @m1 = FactoryGirl.create(:micropost, user: user, content: "Lorem Ipsum")
  @m2 = FactoryGirl.create(:micropost, user: user, content: "Foo Bar")

  visit user_path(@user) 
end

// Use the instance variables in your tests, or wrap them in methods:
let(:user) { @user }
let(:m1) { @m2 }
let(:m2) { @m1 }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top