Rob OlsonRob Olson
Using assert_select and friends in Helper Tests
edit Posted by Rob Olson on Monday July 06, 2009 at 08:19PM

Assertions such as assert_select, assert_tag, and css_select are powerful tools in view tests. Since view helpers generate a chunk of HTML, it is sometimes practical to use these assertions to test their return value.

If you attempt to use assert_select it will fail because there has not been a call to render, as is done in a view test. To get around this you can store the result of the helper method in @response.body. Once @response.body is populated, the assertions will work like expected.

Update: For people using RSpec, Pat Maddox posted a helpful suggestion in the comments showing how to use have_tag to accomplish the same thing in RSpec style.

Example

describe TagsHelper do
  describe "#tag_list"
    describe "when the user is not an admin"

      it "should not have links to delete the tags" do
        @response.body = helper.tag_list
        assert_select "a", 0
      end

    end
  end
end

Reality Check

If you find yourself writing a helper method that generates a lot of HTML, or is overly complex, stop and ask yourself whether it would be more appropriate to move this code into a partial.

Attribution

Credit for this tip goes to a comment by Bill Siggelkow on the Nuby on Rails blog.

Comments

  1. Pat Maddox Pat Maddox on July 07, 2009 at 12:15AM

    Nuby on Rails is erroring as I post this...anyway RSpec provides the have_tag matcher, which just wraps assert_select. Your example would then become:

    describe TagsHelper do
      describe "#tag_list" do
        describe "when the user is not an admin" do
          it "should not have links to delete the tags" do
            helper.tag_list.should have_tag('a', 0)
          end
        end
      end
    end
    

    or the even more pleasant

            helper.tag_list.should_not have_tag('a')
    

    Finally you can assert nested elements using a block and with_tag:

    describe TagsHelper, "#tags_list" do
      it "should build an unordered list of the tags" do
        helper.tag_list.should have_tag('ul') do
          with_tag('li', 3)
        end
      end
    end
    

    (with_tag can itself take a block, best not to go too deep though :)

  2. Rob Olson Rob Olson on July 07, 2009 at 09:46AM

    Thanks for the tip Pat! I had overlooked have_tag when picking up RSpec but it is clearly the RSpec way to write it.

    I imagine that using aforementioned method would still be useful when testing with vanilla Rails tests (sans RSpec).

  3. Larry Marburger Larry Marburger on July 08, 2009 at 06:11AM

    I've been struggling recently with how to best test helpers and partials. I'm sure it's not the best approach, but I've resorted to using helpers as much as possible simply because I can thoroughly test them. Coming from ASP.NET, I'm still carrying with me a lot of baggage. I can't stand concatenating strings of HTML in helpers and have an equal distaste for any logic (basically anything more complex than simply printing a variable) in my views.

    Take, for example, a simple helper and its corresponding test I ripped from a recent project. I created a method in my TestHelper called set_response_body which does exactly what you've done--set @response.body equal to the given string. Then, through a series of assert_select statements, I ensure the resulting markup is correct.

    Personally, I feel there's a lot of logic that can go into generating markup and I'd like to test as much of it as possible. However, I don't think mine is a great solution. As it stands, there's a gaping hole in my tests where I'm sure the markup generated by the helpers are correct but I have no way of knowing if the views are using them appropriately. To accomplish the level of testing I'd like to achieve, I'm starting to realize the real solution would be to use webrat to perform thorough tests on the actual markup that's being sent to the browser.

  4. Stephan Wehner Stephan Wehner on July 08, 2009 at 09:28AM

    Larry, have you tried using mocha's expects for the view testing; something like

    ....expects(:events_list).with(...).returns(...) .. trigger rendering ...

    You might then further check that what is returned appears in the markup.

    Stephan

  5. TJ Stankus TJ Stankus on August 26, 2009 at 11:13AM

    I wrote a blog post on this topic a little while back. It has a link to a plugin and some code I gist'ed. Perhaps it might help someone http://tjstankus.wordpress.com/2009/01/26/testing-html-strings-returned-from-helpers/