Tutorials Point


  Learning Ruby on Rails 2.1
  Advanced Ruby on Rails 2.1
  Ruby on Rails Quick Guide
  Ruby Tutorial
  Ruby on Rails Resources
  Selected Reading

Copyright © 2014 by tutorialspoint



  Home     References     Discussion Forums     About TP  

Ruby on Rails 2.1 - Applications' Unit Testing


previous next AddThis Social Bookmark Button

Advertisements

Introduction:

Before proceeding let's have a quick look of few definitions:

  • The Tests - They are test applications that produce consistent result and prove that a Rails application does what it is expected to do. Tests are developed concurrently with the actual application.

  • The Assertion - This is a one line of code that evaluates an object (or expression) for expected results. For example, is this value = that value? is this object nil?

  • The Test Case - This is a class inherited from Test::Unit::TestCase containing a testing strategy comprised of contextually related tests.

  • The Test Suite - This is a collection of test cases. When you run a test suite, it will, in turn, execute each test that belongs to it.

Rails Testing:

When you run the helper script script/generate to create controllers and models, Rails generate a framework for unit and functional tests. You can get pretty good test coverage by filling in the framework with tests for the functionality you write. There are two important points to test in a Rails application:

  • Testing the Models.

  • Testing the Controllers.

This tutorial will cover both the testings in brief. So let's create one testapp to understand the concept.

C:\ruby> rails -d mysql testapp

Database Setup:

Till now we have used only Rails application's development database, but now you needed to make sure that the testing database is also created and appropriate section of your config/database.yml file was set up correctly.

Let's create development and testing databases as follows:

mysql> create database testapp_test;
Query OK, 1 row affected (0.01 sec)

mysql> create database testapp_development;
Query OK, 1 row affected (0.01 sec)

mysql> use testapp_test;
Database changed

mysql> grant all privileges on testapp_test.* 
          to 'root'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Configuring database.yml:

Configure your config/database.yml as follows:

development:
  adapter: mysql
  encoding: utf8
  database: testapp_development
  username: root
  password: password
  host: localhost
test:
  adapter: mysql
  encoding: utf8
  database: testapp_test
  username: root
  password: password
  host: localhost
production:
  adapter: mysql
  encoding: utf8
  database: testapp_production
  username: root
  password: password
  host: localhost

Generate Migration:

Assume you have a table containing books, including their titles, price and a small description. The following migration sets up this table:

testapp > ruby script/generate migration books

Now modify the testapp/db/migrate/20080616170315_books.rb file as follows:

class Books < ActiveRecord::Migration
  def self.up
     create_table :books do |t|
        t.string     :title, :limit => 32, :null => false
        t.float      :price
        t.text       :description
        t.timestamp  :created_at
     end
  end
  def self.down
    drop_table :books
  end
end

Now run the migration as follows:

testapp > rake db:migrate

This will create books table in testapp_development database. Now after this we need to set up your test database using rake command as follows:

C:\ruby\testapp > rake db:test:clone_structure

This will clone testapp_development database into testapp_test database. It means whatever you have in development database, now you will have in test database as well.

Testing Models:

When you generate a model with the generate script, Rails also generates a unit test script for the model in the test directory. It also creates a fixture, a YAML file containing test data to be loaded into the testapp_test database. This is the data against which your unit tests will run:

testapp > ruby script/generate model Book
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/book.rb
      create  test/unit/book_test.rb
      create  test/fixtures/books.yml
      create  db/migrate
      create  db/migrate/20080616164236_create_books.rb

As you write code in the model classes, you'll write corresponding tests in these files. So let's create two test book records using YAML in test/fixtures/books.yml as follows:

perl_cb:
  id: 1
  title: 'Ruby Tutorial'
  price: 102.00
  description : 'This is a nice Ruby tutorial'
java_cb:
  id: 2
  title: 'Java Programming'
  price: 62.00
  description : 'Java Programming for the beginners'

Now let's relace exsiting code in book unit test file test/unit/book_test.rb with the following code:

require File.dirname(__FILE__) + '/../test_helper'

class BookTest < ActiveSupport::TestCase
  fixtures :books

  def test_book

    perl_book = Book.new :title => books(:perl_cb).title, 
                         :price => books(:perl_cb).price,
                         :description => books(:perl_cb).description,
                         :created_at => books(:perl_cb).created_at

    assert perl_book.save

    perl_book_copy = Book.find(perl_book.id)

    assert_equal perl_book.title, perl_book_copy.title

    perl_book.title = "Ruby Tutorial"

    assert perl_book.save
    assert perl_book.destroy
  end

end

Finally, run the test method as follows:

testapp > ruby test/unit/book_test.rb

Here's the output of running the successful test case:

testapp > ruby test/unit/book_test_crud.rb 
Loaded suite ./test/unit/book_test
Started
.
Finished in 0.0625 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

What has happend here:

  • The BookTest method starts off by creating a new Book object using the title and other fields from the first record in the text fixture/books.yml. The resulting object is stored in the perl_book instance variable.

  • The first assertion tests that saving the Book object was successful.

  • Next, the book object is retrieved using the find method and stored in another instance variable named perl_book_copy. The success of this retrieval is tested in the next assertion, which compares the titles of both book objects. At this point, we've tested the ability to create and read a database record.

  • The solution tests updating by assigning a new title to the object stored in perl_book and then asserts that saving the change is successful.

  • Finally, the ability to destroy a Book object is tested.

This is how we can test our Rails Models.

Testing Controllers:

Controller testing is also known as functional testing. Functional testing tests the following type of functionalities of the controllers:

  • Is the response redirected as expected ?
  • Is the expected template rendered?
  • Is the routing as expected
  • Does the response contain the expected tags?

Rails framework supports 5 types of requests and to write a functional test, you need to simulate any of the five HTTP request types that your controller will process. :

  • get
  • post
  • put
  • head
  • delete

Request type "get" and "post" are the most commonly used in controller testing. All these methods take four arguments:

  • The action of a controller
  • An optional hash of request parameters
  • An optional session hash
  • An optional flash hash

In this tutorial, we will see how to use get method to test our controller. You can test rest of the methods in similar way.

When you generate a controller with generate, Rails creates a functional test script for the controller as follows:

testapp > ruby script/generate controller Book
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/book
      exists  test/functional/
      create  app/controllers/book_controller.rb
      create  test/functional/book_controller_test.rb
      create  app/helpers/book_helper.rb

As you write code in the controller classes, you'll write corresponding tests in these files. Before that let's define our controller functions list, show, and search inside app/controllers/book_controller.rb as follows:

class BookController < ApplicationController
  def list
    @book_pages, @books = paginate :books, :per_page => 10
  end

  def show
    @book = Book.find(params[:id])
  end

  def search
    @book = Book.find_by_title(params[:title])
    if @book
      redirect_to :action => 'show', :id => @book.id
    else    
      flash[:error] = 'No such book available'
      redirect_to :action => 'list'
    end
  end
end

NOTE: You would need two views templates for show and list method. You can define those views and test them, but right we will proceed without defining those views.

Now let's reuse our test fixture which is in test/fixtures/books.yml file as follows:

perl_cb:
  id: 1
  title: 'Ruby Tutorial'
  price: 102.00
  description : 'This is a nice Ruby tutorial'
java_cb:
  id: 2
  title: 'Java Programming'
  price: 62.00
  description : 'Java Programming for the beginners'

Add the following test_search_book and test_search_not_found methods to test/functional/book_controller_test.rb to test the functionality of the Book Controller's search action.

require File.dirname(__FILE__) + '/../test_helper'
require 'book_controller'

# Re-raise errors caught by the controller.
class BookController
   def rescue_action(e) 
      raise e 
   end
end

class BookControllerTest < Test::Unit::TestCase
  fixtures :books
  def setup
    @controller = BookController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_search_book
    get :search, :title => 'Ruby Tutorial'
    assert_not_nil assigns(:book)
    assert_equal books(:perl_cb).title, assigns(:book).title
    assert_valid assigns(:book)
    assert_redirected_to :action => 'show'
  end

 def test_search_not_found
    get :search, :title => 'HTML Tutorial'
    assert_redirected_to :action => 'list'
    assert_equal 'No such book available', flash[:error]
  end
end

Now run your test cases as follows:

testapp > ruby test/functional/book_controller_test.rb 

This gives following output:

Loaded suite test/functional/book_controller_test
Started
..
Finished in 0.422 seconds.

2 tests, 7 assertions, 0 failures, 0 errors

Let's see what has happened here:

  • The setup method is a default method to create controller, request and response objects. They would be used by Rails internally.

  • The first test method test_search_book generates a get request to the search action, passing in a title parameter.

  • The next two assertions verify that a Book object was saved in an instance variable called @book and that the object passes any Active Record validations that might exist.

  • The final assertion inside first method tests that the request was redirected to the controller's show action.

  • The second test method, test_search_not_found, performs another get request but passes in an invalid title.

  • The first assertions test that a redirect to the list action was issued.

  • If the proceeding assertions passed, there should be a message in the flash hash which you can test with assert_equal.

For a complete detail on Assertions ,please refere to Rails Standard Documentation.

Using Rake for testing:

You can use rake utility to test your applications. Here are few important commands.

  • $rake test - Test all unit tests and functional tests (and integration tests, if they exist).

  • $rake test:functionals - Run all functional tests.

  • $rake test:units - Run all unit tests.

  • $rake test:integration - Run all integration tests.

  • $rake test:plugins - Run all test in ./vendor/plugins/**/test.

  • $rake test:recent - Run tests for models and controllers that have been modified in the last 10 minutes:

  • $rake test:uncommitted - For projects in Subversion, run tests for models and controllers changes since last commit:



previous next Printer Friendly

Advertisements


  

Advertisements