Rails Testing: Not Just for the Paranoid
Pages: 1, 2, 3, 4, 5
You'll notice that I call Tag.destroy_all in my setup. This is a workaround for the fact that when you run your unit and functional tests together, it seems as if the fixture data from the unit tests wants to linger. Another option would be to run rake test:functional instead of just rake, but I'm extremely lazy.
Dealing with forms
Though it's helpful to ensure you can at least successfully call an action, usually you'll be more interested in testing out form handling. We're going to design a simple Entry search feature test-first. Writing tests before code can be a very powerful technique, as it requires you to think about design right away rather than as an afterthought.
Let's start by setting up a test that makes sure that you can access a search action:
test/functional/entries_controller_test.rb
def test_search
get :search
assert_response :success
end
If you run the tests now, you'll notice this generated an error:
ActionController::UnknownAction: No action responded to search
In order to make this pass, we need to add an action to our entries controller, for now it doesn't need to do anything:
app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def index
@entries = Entry.find(:all)
end
def search
end
end
If you've already ran your tests after adding this chunk of code, you'll see that we're missing a template. For now that also doesn't need to do anything, it just needs to have an empty file at app/views/entries/search.rhtml.
Once you have that, the tests should be all green, and that means we can make them more interesting. This test adds the expectation that if we pass a parameter for a tag that doesn't exist, it will stick a notice in the flash object.
def test_search
get :search
assert_response :success
get :search, :tag => "not_here"
assert_equal "No Entries With Tag: not_here", flash[:notice]
end
Here's the code that makes that pass:
def search
if params[:tag]
@entries = Tag.entries_by_name(params[:tag])
unless @entries
flash[:notice] = "No Entries With Tag: #{params[:tag]}"
end
end
end
Now we only need to deal with one more case, successful search. Here's our full test:
def test_search
get :search
assert_response :success
get :search, :tag => "not_here"
assert_equal "No Entries With Tag: not_here", flash[:notice]
e = Entry.create(:url => "apple.com")
f = Entry.create(:url => "alsoapple.com")
e.tag_as("apple")
f.tag_as("apple")
get :search, :tag => "apple"
assert_equal "Showing 2 Matches For Tag: apple", flash[:notice]
end
This code will make it pass:
def search
if params[:tag]
@entries = Tag.entries_by_name(params[:tag])
if @entries
flash[:notice] = "Showing #{@entries.length} Matches For Tag: #{params[:tag]}"
else
flash[:notice] = "No Entries With Tag: #{params[:tag]}"
end
end
end



