[rspec-users] Rails view spec expectations/matchers

David Chelimsky dchelimsky at gmail.com
Tue Oct 11 09:21:13 EDT 2011


On Oct 8, 2011, at 1:30 PM, Christopher J. Bottaro wrote:

> >From looking at the RSpec Rails documentation (https://www.relishapp.com/rspec/rspec-rails/docs/view-specs/view-spec) it seems like rendered is just a string and you can't really do any assert_select type stuff out of the box.
> 
> After Googling around, it seems that the RSpec authors decided that if you want that functionality, you should just use Capybara or some such... is that correct?

The `rendered` method is a wrapper around the `@rendered` instance variable, which is provided by Rails: https://github.com/rails/rails/blob/master/actionpack/lib/action_view/test_case.rb#L113

The fact that assert_select doesn't work on a string is an issue you can (and probably should) report to the rails project.

> If that is the case, can you please look at how I'm writing my view specs and let me know if there is a better/cleaner way?
> 
> Here goes...
> 
> spec/spec_helper.rb
> RSpec.configure do |config|
>   config.include RSpec::ViewHelper, :type => :view
> end
> 
> spec/support/view_helper.rb
> module RSpec::ViewHelper
>   def page
>     @page ||= Capybara::Node::Simple.new(rendered)
>   end
> end

ViewHelper sounds like something to help your views, not your view specs. I'd name this something like RSpec::CapybaraExtensions.

I'd also avoid the name "page", which carries different implications in controller and request specs. Suggestions below ...

> spec/views/comments/index.html.haml_spec.rb
> require "spec_helper"
> 
> describe "comments/index.html.haml" do
> 
>   it "should show a proper breadcrumb" do
>     # Assign instance vars
>     # Mock helper methods
> 
>     render
> 
>     page.should have_selector("div.breadcrumb")
>     page.find("div.breadcrumb").tap do |node|
>       node.find_link("Home").should be
>       node.find_link("Blah1").should be
>       node.find_link("Blah1").should be
>       node.find_link("Comments").should be
>     end
>   end
> 
> end
> 
> Is there a better way?

Several! Here's one:

  def rendered
    Capybara.string(@rendered)
  end

  it "displays a proper breadcrumb" do
    render
    rendered.find("div.breadcrumb a", :text => "Home")
    rendered.find("div.breadcrumb a", :text => "Comments")
  end

  it "displays a proper breadcrumb" do
    render
    rendered.find("div.breadcrumb").tap do |form|
      form.find("a", :text => "Home")
      form.find("a", :text => "Comments")
    end
  end

`find` doesn't look like an expectation, but if the elements aren't there you get OK feedback:

  Capybara::ElementNotFound:
    Unable to find css "div.breadcrumb"

You could also make this more expressive like this:

  def rendered
    Capybara.string(@rendered)
  end

  def within(selector)
    yield rendered.find(selector)
  end

  it "displays a proper breadcrumb" do
    render
    within "div.breadcrumb" do |breadcrumb|
      breadcrumb.should have_selector("a", :text => "Home")
      breadcrumb.should have_selector("a", :text => "Comments")
    end
  end

Now you'd get the feedback above if there were no "div.breadcrumb", but you'd get Capy's matcher feedback if there were no matching anchor tags within "div.breadcrumb", which is not very good right now:

  expected css "a" with text "Home" to return something

There is a github issue to address this, but it's no small matter to get right (which is why it's still open): https://github.com/jnicklas/capybara/issues/331

> Including Capybara just for the finders/matchers seems kinda heavy.

Why? Just load up the parts you want.

> Also it would be nice if I could write stuff like node.should have_link("Home"), to be more consistent with RSpec matchers (though I guess I could write custom matchers to wrap all the Capybara stuff... has anyone already done this?).

I'd prefer to stick to `have_selector("a", :text => "Home")`. That way it's the same API for every tag. But that's me. If you prefer have_link, have_text_field, etc, etc, then you should write them, and maybe even publish them in a gem.

HTH,
David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20111011/d2cb7c20/attachment.html>


More information about the rspec-users mailing list