what did i learn today
News content_for view testing yield rails rspec
[rspec] view testing: testing the content_for results

I normally do not do a lot of view specs, but at least I want to make sure that my view renders without errors. And sometimes I really need to make sure that some link is shown or hidden depending on e.g. the role of the user or linked objects.

For example,

 describe "posts/show.html.haml" do
   context "without any comments" do
     it "displays no comments" do
       @post = Factory(:post)
       rendered.should contain(I18n.t(''))

So we check the rendered result, whether it contains a specific text.

But what happens if your view is rendering different yield regions? Like a body (the default region) and a sidebar.

Let's use a view like this :

  = show_for @post do |p|
    = p.attribute :title 
    = p.attribute :content
  - if @post.comments
    = render :partial => 'comments' 
  - else 
    = t('')
  = content_for :sidebar do
    = link_to 'Edit', edit_post(@post) if is_allowed_to?(:edit)

It renders the attributes using the show_for gem, and then renders inside the sidebar a link if the current user is allowed to edit it.

Now I want to test what is rendered into the sidebar. To my dismay I found that neither content_for or content_for?worked at all inside rspec.

And rendered does not contain the data for the other regions.

So somehow I would want to get to the content for :sidebar.

It appears that the different regions are actually stored inside an instance variable of the view. Once I figured that out, the rest was easy:

  describe "posts/show.html.haml" do
    def rendered_content_for(name)
    context "with enough rights" do
      it "displays a link to edit the post" do
        @post = Factory(:post)
        view.stub(:is_allowed_to?) { true }
        rendered_content_for(:sidebar).should contain('Edit') 
    context "with no rights" do
      it "does not display a link to edit the post" do
        @post = Factory(:post)
        view.stub(:is_allowed_to?) { false }
        render rendered_content_for(:sidebar).should_not contain('Edit')

Hope that this helps somebody. Or did you find a better way?

More ...