helper testing using ActionView::TestCase
Testing testing testing. We test our models, our controllers, and the integration between them. But how often do we pay attention to helpers? Not very often, if the test coverage of my current project’s helpers is any indicator.
There’s an even better way, and it’s included with Rails itself since at least 2.1: ActionView::TestCase. The name might be misleading, but it’s intended for testing view helpers. It also is a kind of underdocumented.
You can skip ahead to the summary if you want the dirty details.
Your very first helper test
The Rails test layout doesn’t have an obvious place to put helper tests. I’ll take a cue from helper_test, and say to put them in
test/unit/helpers. That way, they will be run by
The skeleton test
Let’s start out with testing
- We do a typical require of
- I’m requiring ‘action_view/test_case’, because without it, we see an error like
uninitialized constant ActionView::TestCase (NameError). I feel like ActiveSupport should find this automatically, but it isn’t. You can also toss this in
- Placeholder test
What to test
I want to add a helper for displaying a navigation tab. It will just go to index of a controller you specify. The link should have the ‘current’ class if you’re rendering from controller you are making a tab for.
Based on that, we have two contexts to test:
- Making a tab for the current controller
- Making a tab for another controller
Test it first
You’d always be using the helper from a specific controller, so we’ll have a parent context of being in a controller, like
Now that we have some context, and some specifications, let’s translate this into a Shoulda’s terminology.
The parent context doesn’t do anything yet, because we don’t know any implementation details about how it’s going to determine the current controller.
assert_dom_equal, as the name implies, checks that the DOM of the given strings are equivalent. Presumably, this takes care of whitespace, casing, etc.
Let’s run it the first time… RED, because we don’t have any implementation.
First pass at implementing it
Let’s try a first implementation:
- We’re generate a url. This presumes that the there’s a path defined like
events_path, as if by
- Make a
- Make a link to the URL we just generated
Let’s see how this fares… some GREEN, but still RED. Creating a tab for another controller passes, but creating a tab for the current controller fails. That’s because we never determine if we’re on on the current controller. How are we going to do that? Here’s some fun facts towards enlightenment:
- Helpers are
included in the view.
- The view exposes
controller, which is the rendering controller
- ActionController::Base exposes
controller_name, which has a stringified name of the controller, ie ‘events’ for
I want to check the value of
controller.controller_name. We can stub
controller to return an actual
EventsController using mocha:
And… we have the same failures. No wonder, because we didn’t change the implementation. Now to give it another try:
Wait for it, wait for it… GREEN!
I believe that wraps things up. Let’s just do a quick recap:
- Stash tests in
- Name your tests like your helper, ie
- Inherit from
require 'action_view/test_helper'somewhere, probably in
- Your helper will be
included into your test, so you can just use its methods
- If you need to touch stuff that would normally be included in the view, stub it out
assert_dom_equalsto test the output. Alternative, you might use
Testing helpers this way is not quite bullet proof though. While coding this up, I encountered a few tough errors that spit out a stack trace because of a
nil value somewhere down in the Rails stack. For, I tried using
url_for instead of using the named routes, and
ActionView::TestCase didn’t take too kindly to that.