I'm running Rails 3.1.1, RSpec 2.7.0 and HAML 3.1.3.
Say I have the following view files:
app/views/layouts/application.html.haml!!!
%html
%head
%title Test
= stylesheet_link_tag "application"
= javascript_include_tag "application"
= csrf_meta_tags
%body
= content_for?(:content) ? yield(:content) : yield
app/views/layouts/companies.html.haml
- content_for :content do
#main
= yield :main
#sidebar
= yield :sidebar
= render :template => 'layouts/application'
app/views/companies/index.html.haml
- content_for :main do
%h1 MainHeader
- content_for :sidebar do
%h1 SidebarHeader
And the following spec file:
spec/views/companies/index_spec.rbrequire 'spec_helper'
describe 'companies/index.html.haml' do
it 'should show the headers' do
render
rendered.should contain('MainHeader')
rendered.should contain('SidebarHeader')
end
end
When I run RSpec, I get the following error:
1) companies/index.html.haml should show the headers
Failure/Error: rendered.should contain('MainHeader')
expected the following element's content to include "MainHeader":
# ./spec/views/companies/index_spec.rb:7:in `block (2 levels) in <top (required)>'
At first, I thought RSpec was somehow missing the content_for blocks when rendering the view files. However, I was not able to find any issue related to it on RSpec's github repository, so I'm not sure who's to blame here.
One (recent) solution I found is at http://www.dixis.com/?p=571. However, when I try the suggested code
view.instance_variable_get(:@_content_for)
it returns nil.
Using Rspec 2 with Rails 3, in order to write view specs for usage of content_for, do this:
view.content_for(:main).should contain('MainHeader')
# instead of contain() I'd recommend using have_tag (webrat)
# or have_selector (capybara)
p.s. the value of a content_for(...) block by default is an empty string, so if you want to write specs showing cases in which content_for(:main) does not get called, use:
view.content_for(:main).should be_blank
Your spec could be written as:
it "should show the headers" do
render
view.content_for(:main).should contain('MainHeader')
view.content_for(:side_header).should contain('SidebarHeader')
end
This way your spec shows exactly what your view does, independent of any layout. For a view spec, I think it's appropriate to test it in isolation. Is it always useful to write view specs? That's an open question.
Instead if you want to write specs showing what the markup served to the user looks like, then you'll want either a request spec or a cucumber feature. A third option would be a controller spec that includes views.
p.s. if you needed to spec a view that outputs some markup directly and delegates other markup to content_for(), you could do that this way:
it "should output 'foo' directly, not as a content_for(:other) block" do
render
rendered.should contain('foo')
view.content_for(:other).should_not contain('foo')
end
it "should pass 'bar' to content_for(:other), and not output 'bar' directly" do
render
rendered.should_not contain('bar')
view.content_for(:other).should contain('bar')
end
That would probably be redundant, but I just wanted to show how render() populates rendered and view.content_for. "rendered" contains whatever output the view produces directly. "view.content_for()" looks up whatever content the view delegated via content_for().
From the RSpec docs:
To provide a layout for the render, you'll need to specify both the template and the layout explicitly.
I updated the spec and it passed:
require 'spec_helper'
describe 'companies/index.html.haml' do
it 'should show the headers' do
render :template => 'companies/index', :layout => 'layouts/companies'
rendered.should contain('MainHeader')
rendered.should contain('SidebarHeader')
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With