advertisement

Print

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

Pages: 1, 2, 3, 4, 5

Next Pagearrow