Index of All Lessons

View Screencast (Quicktime)

Right-click on the button above and choose Save As to save file to your computer


Support the Course

There’s no charge for the course, but we greatly appreciate any donations.

Suggested donations:

  • One or two lessons: $5
  • Several lessons $10
  • Entire course: $25 – $50

We hope you’ve found the course to be valuable, and we appreciate any support you care to provide.


Sign Up Now!

If you aren’t already receiving our course lessons via email, sign up now to be sure you don’t miss anything.

Every few days, we’ll send you an email with a link to the next episode, plus a list of additional resources for advancing your knowledge.

There’s no cost and no obligation. And we’ll never share your email address with any third party.


We’ll send you the first lesson right away.


Want to help spread the word? We’d be grateful if you would include a link to the course in your blog, web site, or emails.

Testing your site (Part 2)

2 comments

Goals

In this lesson, we finish up our testing journey by fixing up some of our functional tests.

Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left.

Setup

The code in this episode is the same as the ending state of the previous episode and contains all of the fixed tests.

Functional Tests

We continue where we left off, having working unit tests but many broken functional tests.

Fixing the Functional Tests

We now know how to read the test results, so switching our attention to the functional test block results, we see we have our work cut out for ourselves:

FEEFFEEFEEFFEEFFFFEFFFFFFFFF.........FEEEE.

Which in my first run was equivalent to 43 tests, 36 assertions, 20 failures, 13 errors. Ouch.

The Rails scaffold generator does a much better job at creating functional tests than it does with unit tests. Functional tests exercise our controllers. Peeking inside most of the test files in the test/functionals directory, you will note that they follow similar patterns.

So what went wrong? In scanning through the test diagnostics, we see a lot of tests with similar issues. There are many tests with problems with redirects: “Expected response to be a <:success>, but was <302>”. There are many tests where things were not created, updated, or destroyed when expected. Thinking back, the common thread here is that all of these things need you to be logged in, yet, when the tests are automatically generated for us, the generator has no idea about this requirement. Let’s fix that.

The restful_authentication plugin that we used provides a utility module called AuthenticatedTestHelper. This module contains useful methods that you can mix-in to your test classes to simulate things like logging in, precisely what we need. Since we protected most of our administrative functions with those before_filter :login_required checks, that will cause any of our tests that try to directly exercise an action to instead get redirected to a login screen.

Let’s pick one of our controller’s tests to fix up as our example. The others will follow similar patterns and you can look at the final project code to see the changes (or try to make the changes yourself as an exercise).

First, add the authentication system’s helper at the top of the test class. While there, add the user fixtures so we have some test user accounts to log in with:

   class CategoriesController < ActionController::TestCase
      include AuthenticatedTestHelper
      fixtures :users

Now, let’s use the new method we have to log in as the user quentin:

   def test_should_get_index
      login_as :quentin
      get :index
      assert_response :success
      assert_not_nil assigns(:categories)
   end

Run the tests again. The redirect failure on test_should_get_index should now be gone.

Go back to each method in this test class and add the login_as :quentin line at the start of each test method. All of your redirect failures should now be cleaned up.

But, there are still failures to fix. Look through the other functional tests and make the same changes to all of the others that are protected with the before_filter (links_controller_test, messages_controller_test, pages_controller_test, users_controller_test).

For the controllers that are selective about the login requirement, be sure to not login when it isn’t needed. For instance, MessageControllerTest’s new and create tests are not protected by login. See the controller to confirm this.

Run the tests again. We are getting closer. In my run, the results look like this:

  Started
    FEE..EEFEE.E.EEF...E.F................FEEEE.
    Finished in 0.547902 seconds.

Our CategoryControllerTest is responsible for a number of those errors and failures, so let’s clean it up next.

test_should_create_category fails with a pattern that will become familiar to you as you test code that uses validations. Here, the code is trying to make a new category object. The test, however, by default tries to create an empty category. Looking inside of the Category model, we see a validates_presence_of on :title. We better fill in a value for the title:

  def test_should_create_category
    login_as :quentin
    assert_difference('Category.count') do
      post :create, :category => { :title => 'test' }
    end

    assert_redirected_to category_path(assigns(:category))
  end

Go through the rest of the code and find and fix the various create tests that need values.

Some test methods seem to be using fixture data, note the categories(:one) syntax we see in methods like test_should_show_category:

  def test_should_show_category
    login_as :quentin
    get :show, :id => categories(:one).id
    assert_response :success
  end

The generator script preloaded the fixtures with the test data it created when we started. Since we renamed the IDs in our fixture files, we need to tweak all instances of fixture usage to use valid ID symbols:

  def test_should_show_category
    login_as :quentin
    get :show, :id => categories(:ruby).id
    assert_response :success
  end

Go through all of the test code and fix up renamed fixtures now.

Writing a new functional test

There are places where we added methods to our RESTful controllers. For instance, in the LinksController we added the list action. We add a test for it in LinkControllerTest:

  def test_should_get_list
    get :list, :name => 'pageone'
    assert_response :success
    assert_not_nil assigns(:categories)
    assert_not_nil assigns(:pagetitle)
    assert_not_nil assigns(:page)
  end    

In this example, we pass a parameter to the list action. list expects the name of the page so it can be passed through to the get_page_metadata helper we wrote, which itself loads up page and pagetitle instance variables to be used to set the page title and appropriate tabs in our navigation interface. To make this work, we have to include the pages fixture to LinksControllerTest and we tweak the fixture file itself so the with the id one has a name value of “pageone”. (See the source code if this is unclear.)

We apply the same fix to the MessagesControllerTest’s test_should_get_new method. While we are looking at MessagesControllerTest, we see one last failure on the update action. It is expecting a redirect (proper behavior on an update) but instead is getting a 200 response.

Looking at the code for this action, we see that if validation fails when updating attributes, the action simply displays the edit form again, and hence we get an HTTP 200 code. Peeking inside of the default fixture data for messages, we see the problem. Change the email fields to be properly formatted:

one:
  name: MyString
  email: bob@example.com
  company: MyString
  phone: MyString
  subject: MyString
  body: MyText

two:
  name: MyString
  email: two@example.com
  company: MyString
  phone: MyString
  subject: MyString
  body: MyText    

Final cleanup

We lost a valuable piece of information when we fixed all of those authentication tests. We really should also test that the right thing happens when someone is not logged in. Let’s write a quick example of a test that succeeds when an unauthenticated person tries to get a protected resource. In LinkControllerTest, add this test:

   def test_should_not_get_index_not_logged_in
      get :index
      assert_redirected_to new_session_path
   end

Here, we are not logged in, and we are trying to get the index of links. We should not be able to do this, and we expect to be redirected to the log-in screen. The assert_redirected_to new_session_path uses the RESTful route to the session controller’s new action. This is where login is initiated.

Run the test, and success!

Further Exercises

  1. Install the ZenTest gem and take autotest for a spin (gem install ZenTest). This will continually run your tests as you code and save files. A real time saver.
  2. Install tarantula to explore integration testing and data fuzzing
  3. See our Testing topic for many other articles

Add a Comment

Have a comment or question about this lesson? Add it here.






Comments on This Lesson

From: Chris Porter       Date: 07/13/08 01:13 PM

Subject: Great work

Thanks for these amazing screencasts.

From: Marcos Ricardo       Date: 07/07/08 01:13 PM

Subject: Course syllabus

Hi Michael and Christopher,

I would like to know, if possible, how long is the way we are going on here.

How many lessons you have planned to this course ?

I just want to know if we are close to the middle or close to the end of the course.

Thanks in advance.

 

Hosting Provided By

EngineYard.com fully managed Rails hosting

Sponsored By

New Relic Rails Performance Monitoring

FiveRuns Tuneup

Peepcode Screencasts