Views
View Examples live in $RAILS_ROOT/spec/views/.
Spec::Rails supports the specification of views in complete isolation from their controllers. This allows you to spec and write your views even before their controllers exist, or before the related actions have been developed. It also means that bugs introduced into controllers will not cause view specs to fail.
As noted in the introduction, these are great benefits but they could hide bugs that exist in the interaction between a controller and its view. We strongly recommend combining these isolated view specs with some sort of high level integration testing, ideally using RSpec stories
Here are some of the methods available to you, but see Spec::Rails::Expectations for more detail.
Conveniences
assigns
Use assigns[:key] to set instance variables to be used in the view. We highly recommend that you exploit the mock framework here rather than providing real model objects in order to keep the view specs isolated from changes to your models.
# example article = mock_model(Article) article.should_receive(:author).and_return("Joe") article.should_receive(:text).and_return("this is the text of the article") assigns[:article] = article assigns[:articles] = [article] # template <% for article in @articles -%> <!-- etc -->
flash, params and session
Use flash[:key], params[:key] and session[:key] to set values in the example that can be accessed by the view.
# example flash[:notice] = "Message in flash" params[:account_id] = "1234" session[:user_id] = "5678" # template <%= flash[:notice] %> <%= params[:account_id] %> <%= session[:user_id] %>
Expectations
Spec::Rails’ View Examples support the following custom expectations.
template.expect_render/stub_render
This is a custom mock-like expectation that allows you to set expectations about partials and included
files that will be rendered, intercepting those calls to the #render method, while ignoring other calls
and passing them on to ActionView::Base. expect_render is verified at the end of the
example, while stub_render is not.
WARNING: expect_render and stub_render, while very useful, act
differently from standard Message Expectations (a.k.a. mock expectations), which would never pass calls
through to the real object. This can be very confusing when there are failures if you’re not aware of this
fact, because some calls will be passed through while others will not. This is especially confusing when
you use stub_render because, as with all Method Stubs, you will get very little feedback
about what is going on.
template.expect_render(:partial => 'person', :object => @person) #auto-verified template.stub_render(:partial => 'person', :object => @person) #not verified
response.should have_tag
This wraps assert_select and is available in both View Examples and Controller Examples run in integration mode.
response.should have_tag('div') #passes if any div tags appear response.should have_tag('div#interesting_div') response.should have_tag('div', 'expected content') response.should have_tag('div', /regexp matching expected content/) response.should have_tag('form[action=?]', things_path) response.should have_tag("input[type=?][checked=?]", 'checkbox', 'checked') response.should have_tag('ul') do with_tag('li', 'list item 1') with_tag('li', 'list item 2') with_tag('li', 'list item 3') end
Note that any of the hash values can be either Strings or Regexps and will be evaluated accordingly.
response[:capture].should have_tag
This way you can access content that has been captured with content_for.
# example response[:sidebar].should have_tag(‘div’) # template <% content_for :sidebar do %>Sidebar content here
Mocking and stubbing helpers
If you wish to mock or stub helper methods, this must be done on the template object:
template.should_receive(:current_user).and_return(mock("user"))
WARNING: Do NOT use mocks on the template object for expecting partials. Instead, use expect_render or stub_render, described above.
Sample View Examples
require File.dirname(__FILE__) + '/../../spec_helper' describe "/people/list" do before(:each) do @smith = mock_model(Person) @jones = mock_model(Person) @smith.stub!(:name).and_return("Joe") @jones.stub!(:name).and_return("Joe") assigns[:people] = [@smith, @jones] end it "should display the list of people" do @smith.should_receive(:name).exactly(3).times.and_return("Smith") @jones.should_receive(:name).exactly(3).times.and_return("Jones") # Careful - this renders 'app/views/people/list.html.erb', not 'http://localhost/people/list' render "/people/list" response.should have_tag('ul') do with_tag('li', 'Name: Smith') with_tag('li', 'Name: Jones') end end it "should have a <div> tag with :id => 'a" do render "/people/list" response.should have_tag('div#a') end it "should have a <hr /> tag with :id => 'spacer" do render "/people/list" response.should have_tag('hr#spacer') end end </div>
