Help to spec a rails helper method that uses content_for

Hi guys,

I am a Rspec newbie and am having problems creating a spec for a RoR
helper
method.

I have in fact already implemented the helper method and am trying to
create
retrospective specs. I know its the wrong way to do it but I am still
learning Rspec.

In my view I have a call to a helper method.

<% body_class “users” %>

which sets a CSS class declaration in the content_for block which is
then
used in the tag in application.html.erb layout.

def body_class(class_name)
content_for(:body_class) { " class="#{class_name}"" }
end

I was trying the following in my view spec:

template.should_receive (:body_class).with(“users”).and_return(’
class=“users”’)
render “/admin/users/index.html.erb”, :layout => “application”
response.should have_tag(‘body.users’)

I get a:

Expected at least 1 element matching “body.users”, found 0.
is not true.

when I printed the response.body to screen it does not contain he CSS
class
declaration on the body tag, where as running mongrel and going to the
page
everything is fine.

what am I doing wrong? I ended up doing:

render “/admin/users/index.html.erb”, :layout => “application”
response[:body_class].should == ’ class=“users”’

I am interested to know why my first attempt did not work.

Ben,
You’re trying to test more than one thing in one spec. The gutter
that’s steered you into here is that you’re looking for side-effects
of a method you’ve mocked. Your spec says to make sure the template
calls “body_class” and to have the call return a string, but that’s
not how the body_class method works: the real method passes that
string along to “content_for,” which is what causes it to end up in
the layout.

There are really three responsibilities to specify: (1) the template
is supposed to provide the right body_class, (2) the body_class method
is supposed to format a string and pass it along to content_for, and
(3) the layout is supposed to put that content into the body tag.

For purposes of specifying the template’s responsibility, I think
you’ve got all you need by saying
template.should_receive(:body_class).with(“users”)

For the helper, you can just say in your helper spec
self.should_receive(:content_for).with(:body_class, "
class=“funclass”")
body_class “funclass”
Note that I’m mocking the receiver of the method being tested, which
is a smell, but I think it’s reasonable when dealing with app-specific
helpers that rely on Rails helper methods. Also note that I’m
specifying that you change your implementation to pass the content as
the second argument to content_for. That’s a lot less ugly to mock
than the version with the block.

I don’t have a good recommendation for specifying the layout’s
responsibility for plopping the “body_class” content into the body
tag, but I think it ought to be pretty easy to do readably. Maybe
someone with more view-spec experience could help out.

-hume.

John,

I know this is quite old, but I wanted to apply your same example in
Rspec 2. I’m getting the following when I attempt to do so.

application_helper.rb

module ApplicationHelper

Return a title on a per-page basis.

def title(page_title)
content_for(:title) { page_title }
end

end

application_helper_spec.rb

require ‘spec_helper’

describe ApplicationHelper do

describe “#title” do
it “should pass through page title to tile variable” do
self.should_receive(:content_for).with(:title, “funtitle”)
title “funtitle”
end
end

end

Results

  1. ApplicationHelper#title should pass through page title to tile
    variable
    Failure/Error: title “funtitle”
    #RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xb0b4fb8
    received :content_for with unexpected arguments
    expected: (:title, “funtitle”)
    got: (:title)

    ./app/helpers/application_helper.rb:5:in `title’

    ./spec/helpers/application_helper_spec.rb:9:in `block (3 levels)

in <top (required)>’

Can you see anything I’m doing wrong?

Thanks,
Paul

On Tue, May 24, 2011 at 10:34 PM, Paul P. [email protected] wrote:

def title(page_title)

Results
# ./spec/helpers/application_helper_spec.rb:9:in `block (3 levels)
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

You actually want to use the “helper” method in helper specs. It is an
instance of ActionView::Base with the described helper module mixed in.
Try
this:

helper.should_receive(:content_for).with(:title, “funtitle”)
helper.title “funtitle”

More info:
http://relishapp.com/rspec/rspec-rails/v/2-6/dir/helper-specs/helper-spec