Friday, July 07, 2006

Testing Helpers and Helping Testers

The Rails helper_test plugin [1] is a useful thing that provides you with more tools to better test-cover your code. View helpers that live in app/helpers should be tested in isolation from unit and functional/integration tests. This is true just on the face of it. But additionally, you get to access Rails helper methods which you are usually combining with your own stuff. Hard to do that in normal unit tests, and the setup in integration tests is a little too bloated for my taste.

There are a few tricks that can be used to make this run even smoother. The first trick is to utilize assert_tag to check your HTML generation. assert_tag wants to check the body in the @response object which is expected to be a valid XHTML string. But you are not in the request/response environment of functional/integration tests, so @response is not available. Not to worry, we can just fake it out for now. Create a class with an accessible instance variable that is an instantiated string named body:

class Response
attr_writer :body
def initialize
@body = ""

def body
"<x>" + @body + "</x>"

In your test method, assign @response = Assign the output of your helper method to @response.body. Now assert_tag will work in all it's glory.

The next trick involves testing the image_tag helper. If my helper generates one or more img tags, they can be hard to test with assert_tag. The reason for this is due to the change in Rails to help the various asset tags work better with browser side caches. The helper
image_tag "graphic.gif"
results in something similar:
<img src="/images/graphic.gif?12345678">

where the parameter after the ? is a file based timestamp. This helps the browser to decide to get a new copy of the asset or not. But calling assert_tag :tag => 'img', :attributes => {:src => "/images/graphic.gif?12345678"} is brittle, since if you update the timestamp, (say the next time you get an update from Subversion,) your tests will break. The easy solution to this is to force the ASSET_ID to always be a fixed string. Add this line to config/environments/test.rb:


Now this will be constant in your tests:

def test_image_asset
@response =
@response.body = my_helper_that_returns_an_img_tag
assert_tag :tag => "img", :attributes => {:src => "/images/graphic.gif?12345"}