Ruby on Rails 3 Testing

Artifacts for Testing Under test

Sub-directories Description
fixtures Contain testing data
functional Testing for individual controllers
integration Integration test for multiple controllers
test_helper.rb Testing configuration
unit Unit testing for models

Ruby on Rails Test Fixtures

Before running tests, Rails load the testing DB data with a pre-defined data called test fixtures

For unit and functional test, by default, load all data under texts/fixtures:

  • Remove any data from the DB table corresponding to the fixture
  • Load the fixture data into the table
  • Load the fixture data into a variable

Test fixtures (accounts.yml)

  user_name: John Smith
  description: My user 1 description
  premium: false
  income: 100000
  ranking: 1.5
  fee: 9.99
  birthday: 2000-02-21
  login_time: 2011-02-21 18:07:36

  user_name: Tom Jones
  description: My user 2 description
  premium: false
  income: 80000
  ranking: 2.3
  fee: 9.99
  birthday: 1990-05-21
  login_time: 2011-02-21 18:07:36
  • Each fixture starts with a name followed by indented key/value pairs
  • Data is separated by a blank space
  • # in the first column comment out the whole line

Adding Ruby code in text fixture

<% base_income = 100000 %>
  income: <%= base_income * 0.9 %>
  login_time: <%= 10.days.ago.to_s(:db) %>

  income: <%= base_income * 0.85 %>
  login_time: <%= 15.days.ago.to_s(:db) %>

Accessing Test Fixture Data

Access from a predefined variable


Access from the DB


Create 1-to-Many Association Test Fixture Objects

Order composes of 1-to-many items


  order_name: book purchase

  order_name: cloth purchase


  item_name: ruby book
  order: order1

  item_name: rails book
  order: order1

  item_name: jeans
  order: order2

Create Many-to-many Association Test Fixture Objects

Vendors have many clients (or vice versa)


  vendor_name: SEV enterprise

  vendor_name: SCO enterprise

clients.yml with the assocaition

  client_name: UIX LCC
  vendors: sco, sev

  client_name: Coco LCC
  vendors: sco

Ruby on Rails Unit Test


require 'test_helper'

class VendorTest < ActiveSupport::TestCase

   # Use fixtures data vendors and clients
   fixtures :vendors, :clients

   test "check data is valid" do

     vendor =

     # Assert not valid since vendor_name is required
     assert !vendor.valid?

     vendor.vendor_name = "E1"
     assert vendor.valid?

     # Compare value
     assert_equal vendor.vendor_name, "E1"


   test "check associations" do
     # Retrieve data from the fixture
     sco = vendors(:sco)

     # Assert SCO should have 2 clients
     assert (sco.clients.length == 2)

   test "check assoication data" do
     sco = vendors(:sco)
     uix = clients(:uix)

     # Get vendors of uix
     companies = uix.vendors

     # Check if sco is one of the vendors
     assert (companies.include? sco)

     coco = clients(:coco)

     # Assert coco vendors is sco
     assert_equal coco.vendors, [sco]


Rails Assert Function Description
assert false Assert based on a boolean value
assert_not_nil assigns Assert controller has set the value of the named variable
assert_response :success for http 200, :redirect for 300-399, :missing for 404, :error for 500-599
assert_redirected_to Assert where the request redirect to
assert_difference Assert the row counts changed by n (Default 1)
assert_equal obj1, obj2 obj1 == obj2
assert_not_equal obj1, obj2 obj1 != obj2
assert_same obj1, obj2 obj1.equal?(obj2)
assert_not_same obj1, obj2 not obj1.equal?(obj2)
assert_nil obj obj.nil?
assert_not_nil obj not obj.nil?
assert_match regexp, string Match a regular expression
assert_no_match regexp, string Not match a regular expression
assert_in_delta expecting, actual, delta Actual is within the delta range of expected
assert_throws :SomeError {...} Expect SomeError is thrown
assert_raise ex1, ex2, ... {...} Expect one of the exception is thrown
assert_nothing_raised ex1, ex2, ... { ...} Expect none of the exception in the list is thrown
assert_instance_of class, obj obj is an instance of class
assert_kind_of class, obj obj is kind of class
assert_respond_to obj, :some_method obj implement some_method
assert_operator obj1, operator, obj2 obj1.operator(obj2)
assert_valid record The model object is valid
assert_difference expressions, difference {...} The change of value in expression before and after the code block
assert_no_difference expressions {} Assert no different in the expression before and after the code block
assert_send array Execute a method
flunk Fail a test
assert_recognizes Asserts that the routing of the given path is correct - Testing the Rails routing data
assert_generates Asserts that the generated path is correct - Testing the Rails routing data
assert_template Asserts that the request was handled by the right template file

The assert methods above an optional "message"

assert false, "Failed"

Run the Unit test

Prepare the DB schema

rake db:migrate

Recreate the testing environment DB with db/schema.rb

rake db:test:load

For sub-sequent testing

rake db:test:prepare
  • Check for available migrations and warn the user
  • Load the test schema

Testing rake task

Rake Tasks Description
rake db:test:purge Empty the test database
rake db:test:clone Recreate the test database from current environment
rake db:test:clone_structure Recreate the test databases from the development environment

Run individual test or method

cd ctest
ruby unit/account_test.rb

ruby unit/account_test.rb -n test_register

Run all unit tests

rake test:units

If test "check assoication data" fails, the following message will be displayed

test_check_assoication_data(VendorTest) [/test/unit/vendor_test.rb:46]:
<false> is not true.

Ruby on Rails Functional Test


require 'test_helper'

class VendorsControllerTest < ActionController::TestCase

  test "should get index" do
    # Send a Http Get request to the "index" action
    get :index

    # Assert controller return a HTTP success code
    assert_response :success

    # Assert controller has set a value for the "vendors"
    assert_not_nil assigns(:vendors)

  test "should get new" do
    get :new
    assert_response :success

  test "should create vendor" do

    # Send a Post request to the "create" action with data
    # Assert the total vendor in the DB has increased by 1 (default value)
    assert_difference('Vendor.count') do
      post :create, :vendor => {:vendor_name => "E3 Enterprise" }

    # Assert where it is re-directed to
    assert_redirected_to vendor_path(assigns(:vendor))

  test "should show vendor" do
    # Set the http parameter id of the vendor
    # Call "show"
    get :show, :id => vendors(:sev).to_param
    assert_response :success

  test "should get edit" do
    get :edit, :id => vendors(:sev).to_param
    assert_response :success

  test "should update vendor" do
    # Update vendor name
    put :update, :id => vendors(:sev).to_param, :vendor => {:vendor_name => "E3 LLC" }
    assert_redirected_to vendor_path(assigns(:vendor))

  test "should destroy vendor" do
    # Assert row count decrease by 1
    assert_difference('Vendor.count', -1) do
      delete :destroy, :id => vendors(:sev).to_param

    assert_redirected_to vendors_path

Simulate a HTTP GET request on a controller's action (for example, show) with pass-in parameters for params

get(:show, {'id' => "133", 'some_param' => "a"})
  • post, put, head, delete method is also available

Passing params and session data

get(:show, {'id' => "133"}, {'some_session_data' => "a"})

Passing params, session data and flash data

get(:show, {'id' => "133"}, {'some_session_data' => "a"}, {'some_flash' => 'flash'})

Hash that can be accessed after the action is completed

assigns["something"]      # Accessing @something

Objects that can be accessed


Make sure the action completed successfully

assert_response :success

Make sure @account is not nil after the action is completed

assert_not_nil assigns(:account)
  • Note assigns:account is invalid for legacy reason

Run the Functional Test

Reload data from the Development DB Schema

rake db:test:prepare
rake test:functionals

Testing 1-to-Many Relationship

class ItemsControllerTest < ActionController::TestCase
  test "should get index" do
    # Send a Get request to the "index" action
    # Instead of just get :index
    get :index, :order_id => orders(:o23).id

    assert_response :success
    assert_not_nil assigns(:items)

  test "should create items" do
    assert_difference('Item.count') do
      post :create, :item => {:item_name => "java" }, :order_id => orders(:o23).id
    assert_redirected_to order_item_path(orders(:o23), assigns(:item))

Ruby on Rails Views Test


assert_select(element, [equality], [message])
assert_select(element, selector, [equality], [message])


assert_select 'title', "Page title"
  • Assert the title of the page is correct

Nested select

# Assert that there is 4 "li" nested under "ol" in the whole HTML document
assert_select "ol" do
  assert_select "li", 4

# For each "ol" element, there are 2 "li" element
assert_select "ol" do |elements|
  elements.each do |element|
    assert_select element, "li", 2


assert_select 'title', "Page Title"  # Assert the page title is
Other method Description
assert_select_encoded Assertion on encoded HTML
css_select Use CSS selector to select element

Ruby on Rails Integration Test

Generate an integration testing skeleton

rails generate integration_test vendors
      exists  test/integration/
      create  test/integration/vendors_test.rb


require 'test_helper'

class VendorsTest < ActionController::IntegrationTest
  fixtures :accounts

  test "register" do
    get '/vendors/new'

    # check there are text input boxes for vendor_name
    assert_select "input[type=text][name='vendor[vendor_name]']"

    assert_difference('Vendor.count') do
      post '/vendors/create', :vendor => {
        :vendor_name => "INN LCC"

    assert_redirected_to "/vendors/#{assigns(:vendor).id}"

    # Search the HTML output contain the regular expression
    assert_select "p", /INN LCC/

Integration Testing Methods

Method Description
https? Is a simulated HTTPS request
https! Simulate a HTTPS request
host! Set the host name
redirect? Last request was a redirect
follow_redirect! Follows a single redirect
request_via_redirect Make an HTTP request and follow the next redirects
post_via_redirect Make a HTTP POST request and follow the next redirects
get_via_redirect Make a HTTP GET request and follow any subsequent redirects
put_via_redirect Make a HTTP PUT request and follow any subsequent redirects
delete_via_redirect Make a HTTP DELETE request and follow any subsequent redirects
open_session Opens a new session

Run Integration Test

rake test:integration

Rake Testing Target

Target Description
rake test Runs all tests
rake test:benchmark Benchmark the performance tests
rake test:functionals Runs all functional tests
rake test:integration Runs all the integration tests
rake test:plugins Run all the plugin tests
rake test:profile Profile the performance tests
rake test:recent Tests recent changes
rake test:uncommitted Runs all the tests which are uncommitted to Subversion
rake test:units Runs all the unit tests

Route Unit Testing

Assert the options generated the specific path

assert_generates("/accounts/1", { :controller => "accounts", :action => "show", :id => "1" })

Assert Rails routes the given path to the specific controller/action

assert_recognizes({ :controller => "accounts", :action => "show", :id => "1" }, "/accounts/1")
assert_recognizes({ :controller => "accounts", :action => "create" }, { :path => "accounts", :method => :post })
assert_recognizes new_account_url, { :path => "accounts", :method => :post }

Combined check for assert_generates & assert_recognizes

assert_routing({ :path => "accounts", :method => :post }, { :controller => "accounts", :action => "create" })

Test setup/teardown

  def setup
     # Run before each test

  def teardown
     # Run after each test

Use a method symbol

 setup :method1

 def method1

Ruby on Rails Performance Testing

Default performance testing file


Generate testing template

rails generate test unit:performance accountpage

create  test/performance/accountpage_test.rb
require 'test_helper'
require 'rails/performance_test_help'

class AccountPerformanceTest < ActionController::PerformanceTest
  def setup
    # Login

  def test_account_home
    get '/accounts'

  def test_creat_new_account
    post '/accounts', :post => { :body => 'testing' }

  def test_some_method
    # Add any Ruby on Rails testing codes

Benchmarking the test

rake test:benchmark
  • Will run the test 4 times by default
  • Test results are generated in /tmp/performance directory
  • Result are appended to previous test to show historical trend
  • Generate a CSV report and a plain report
  • Test are run in testing environment

Profiling the test

rake test:profile

Command Line Tool

Benchmark methods

rails benchmarker 10 'Account.all' ''

Profile methods

rails profiler 'Account.all' 5
  • Run the test 5 times

Rails Performance Helper Method

Benchmark model

Project.benchmark("Benchmark model") do
  project = Project.create("name" => "Test2")
  project.create_manager("name" => "Jonathan")
  project.milestones << Account.find(:all)
  • Information is logged in the log file
  • Log file contains other performance information logged by Rails

Benchmark controller

def process_projects
  self.class.benchmark("Processing projects") do

Benchmark a view

<% benchmark("Benchmark view") do %>
  <%= render :partial => @projects %>
<% end %>