Unit and Functional Testing for Rails

From Science IT

Jump to: navigation, search

Rails comes with a test library for developers to write scripts to determine if all parts of the application are working as they should be.

It's encouraged that a Rails developer will write the tests for any Rails application as it is in the process of being developed. Once a model or controller has had a change made and it works, parallel tests should be written to predict what should work and what should fail. This way you will build a test suit that can be run at any time against the application during development and afterward.

The test library can only test server-side parts of the application and can't test the application's JavaScript.



Setting up

Preparing test database

  1. Setup a database that Rails can use for test data. The test database will ensure any changes to the data made will not affect development or production data.
  2. Update the database details for the test environment in config/database.yml.
  3. Ask Rails to clone the development database to the test database. From the Rails application root, run:
rake db:test:prepare

Preparing fixtures

Rails tests use fixtures (YAML data) for populating data in the test database.

Fixtures should be created in [RAILS_ROOT]/test/fixtures

Name the fixture file the same as the database table.

The content of the test tables gets wiped for each test, and this YAML data gets loaded before each test.

The YAML fixtures for each table we will need in the tests need to be explicitly loaded in test/test_helper.rb).

fixtures :machine, :im_deployments, :ips # fixture_file_name ...

Where a database table has been named differently from what Rails expects, you will need to add a line similar to the following to test/test_helper.rb as well.

set_fixture_class :im_deployments => Deployment # (fixture_file_name => ModelClassName)


If the table names don't match the Rails model names, and the table names have been set using set_table_name in the Models, see this document.

Functional Tests (Testing controllers and views)

Functional tests interact with actions in the Rails controllers. They can test what variables each action prepares for the view, can test errors that Rails might raise, and test the HTML that would get rendered to the view.

Functional tests should be created in the directory 'test/functional' with the name convention of [controller_name]_test.rb. For example, the functional test script to test actions in lab_controller.rb would be:


By default Rails will create this file for you when you generated a controller called 'lab'.

There is a site that has some good documentation but in a nutshell, here is an example of a simple test script:

# this line will be auto generated for you when you make the controller in Rails
 require File.dirname(__FILE__) + '/../test_helper'
 # require the controller we are testing
 require 'lab_controller'
 # this line modifies the controller we are testing to re-raise any exceptions caught during our test.
 class LabController; def rescue_action(e) raise e end; end
 class LabControllerTest < ActionController::TestCase
    # setup() will execute before every test, it is recommended for 99% of tests to just copy this one
    def setup
        @controller = LabController.new
        @request = ActionController::TestRequest.new
        @response = ActionController::TestResponse.new
    # all tests that will run will be methods beginning with the name convention 'test_'
    # each test will run one after the other. it's common to have very long test_ method names
    # since these methods will never be called by a human.
    def test_that_the_index_page_returns_correct_html
        # HTTP GET the 'index' action of the controller we are testing
        # any variables in hash are considered parameters that will be passed to the action
        # the following will be equivalent to visiting the URL lab/index?id=1&another=test
        get :index, { :id => 1, :another => 'test' }
        # test class variables, these are the same as what would be passed to the view.
        # all class variables are available in a hash called 'assigns', 
        # i think to avoid collisions with any class variables in the test script.
        # assigns['lab'] will be equivalent to @lab in the controller we are testing, 
        # and assigns['lab'].id equivalent to @lab.id
        assert_not_nil assigns['lab']
        assert_equal 1, assigns['lab'].id
        # we can test the HTML of the view that would be rendered.
        # the following will test in the generated HTML view there will be a div tag with a class of 'fieldWithErrors'
        assert_tag :tag => 'div', 
            :attributes => {
                :class => 'fieldWithErrors'
        # test response of action
        assert_response 200
        # test that the correct view file would be rendered
        assert_template 'lab/index'
    # this is our second test
    def test_routes
        # assert_routing lets you test the routes defined in config/routes.rb
        assert_routing "lab/1/inspect", { :controller => 'lab', :id => '1':action => 'index' }

All assert_ methods will either pass or fail. For a complete list of all assert_ methods you can use in your tests see the Rails Assertions class documentation, or download a Rails Assertions cheat sheet (PDF).

Unit Tests (Testing models)

Unit tests are very similar to functional tests, except they test the models of your Rails application instead of controllers. Unit tests are in the directory 'test/unit/' and are generated automatically when you generate a new model.

Below is a simple example of a script 'test/unit/machine_test.rb':

require File.dirname(__FILE__) + '/../test_helper'
 class MachineTest < ActiveSupport::TestCase
    # setup() will run before each method
    def setup
       @machine = Machine.new
    def test_save_with_valid_data
        @machine.name = '**unit test**'
        assert @machine.save
        assert @machine.errors.empty?
    def test_save_with_invalid_data
        @machine.name = nil
        assert !@machine.save
        assert @machine.errors.size > 0

Running the Tests

In the Rails application root:

rake test

Or, to run just functional or just unit tests:

rake test:functionals
rake test:units

Run tests for controllers and models modified in the last 10 minutes:

rake test:recent

Run tests for controllers and models modified and not yet checked into subversion:

rake test:uncommitted

Reading test results

After the tests have run you will get an output like this:

9 tests, 58 assertions, 1 failures, 0 errors

If a test failed, information about why the test failed will be included in the output but can be difficult to analyze because of how much gets printed. These are the things to scan for.

The first part will tell you the method where the failure occurred in your functional test script. (test_save_lab), and below that the line number of where the failure occurred (263):

 1) Failure:
   [./test/functional/lab_controller_test.rb:263:in `test_save_lab'

A few lines down will be output what was expected, but what the test actually produced instead:

expected tag, but no tag found matching {:tag=>"no_div"} in:

At the end will be a simple statement of why it failed

<nil> is not true.


This page was last modified on 27 February 2009, at 15:00. This page has been accessed 9,949 times.

Personal tools