Archive for July 3rd, 2007

July 3rd, 2007

More Rails Test Links

by Tim Cull

Thought I’d add these two links for reference:
A list of available assertions in Ruby
A list of available assertions in Rails

July 3rd, 2007

Rails Unit Testing is Great, Once You Figure it Out

by Tim Cull

After a couple of months away from it, I’ve picked up my Ruby on Rails project again. The first thing I wanted to do was make an honest developer of myself and make my build pass–up until now I’d been allowing myself to think of my project as experimental and was cutting a lot of corners just to figure things out.

First, let me state that the unit testing that’s built into Ruby and into Rails is exceptional. Without having to even think about it, the default build target will run your unit tests, functional tests, and insert and tear down data fixtures lickety split. After going to a session by Kevin Clark on unit testing in Rails at SD West, I was even farther ahead.

My first problem, though, was the point at which I wanted to do something slightly unusual like check for the presence or absence of a variable in the session. Now, I’ve been doing most of my development offline in coffee shops or on the bus, so I didn’t have access to excellent Rails unit testing tutorials online. I was on my own.

I did, however, have this skeletal unit test from the SD West session:

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

class PostsController; def rescue_action(e) raise e end; end

class PostsControllerTest < Test::Unit::TestCase
  fixtures :posts

  def setup
    @controller = PostsController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_show
    get :show, :id => posts(:ruby).id
    assert_response :success
    assert_equal posts(:ruby), assigns(:post)  # assigns looks at instance variables inside the controller
    assert_match( /#{posts(:ruby).title}/, @response.body)
  end

  def test_create
    startcount = Post.count
    post :create, :post => {:title => 'Hi SD West', :body => 'Here I am'}
    endcount = Post.count
    assert_equal 1, endcount - startcount
    assert_redirected_to :action => 'show', :id => assigns(:post)
  end
end

Here, the assigns hash will contain any instance variables I set in the controller. I figured, hell, there must be a session hash, too. So I tried this:

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

class HomeController; def rescue_action(e) raise e end; end

class HomeControllerTest < Test::Unit::TestCase
  fixtures :registered_users

  def setup
    @controller = HomeController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new

    @first_user = registered_users(:one)
  end

  def test_list
    #should fail if there's no registered user in the session
    get :list
    assert_response :success
    assert_template 'login'
    assert_nil session[:registered_user]

  end

end

Nope, didn't work. After much fumbling and messing around, I finally discovered that the session lives in that mysterious @response variable, as below. And this whole exercise has exposed to me my greatest frustration with Ruby (and other dynamic languages like Python, PHP, etc): if it's not strongly typed, then your IDE can't do much to help you. If I had been working in Java, I could have typed "super.<ctrl -space>" or "@response.<ctrl -space>" or etc. in Eclipse and seen immediately what the available methods and fields were. I didn't realize before just exactly how reliant I'd become on that before I started using more dynamic languages this last few years. I guess you can't have your cake and eat it, too.

For posterity, here is the real solution:

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

class HomeController; def rescue_action(e) raise e end; end

class HomeControllerTest < Test::Unit::TestCase
  fixtures :registered_users

  def setup
    @controller = HomeController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new

    @first_user = registered_users(:one)
  end

  def test_list
    #should fail if there's no registered user in the session
    get :list
    assert_response :success
    assert_template 'login'
    assert_nil @response.session[:registered_user]

  end

end