Fork me on GitHub
Hoopla! - now with extra whiz-bang home

I decided to try filling up my ./test/integration folder today because it was empty and I felt I must have been missing out. I can't stand the idea that all the other Rails devs are enjoying some feature I haven't found out about yet.

Integration tests seem pretty handy because functional tests are pretty basic - effectively unit tests for controllers rather than tests of real interactivity. So I figured I'd google around a bit to discover this integration testing thing and this is what I found:

Rails Source regarding integration tests

The original announcement on the rails weblog

Jamis Buck's personal blog (which currently doesn't have a web server so I had to grab the page from Google's cache).

That's it. Those three meager pages are all the info I could find on Rails integration testing. In fact, I have reason to believe that the Rails Recipes book just re-used the code from Jamis Buck's post - so even they don't have any more for us.

Well, that's not acceptable. I'm trying to follow a particular user's experience through my app and I want to get this working without having to resort to SeleniumOnRails or some other browser-emulator.

My code so far

I've got a particular user type Athlete that logs in and checks their profile. They update a couple pieces of information and we check that the info was saved. Should be easy. (note: to understand this code check out Jamis's article - you might need to look in the Google Cache for it)

<type:code>

class AthleteSessionTest < ActionController::IntegrationTest

fixtures :users,

        :athlete_contacts,
        :coach_contacts,
        :junior_colleges,
        :combines

def test_randy_visits

randy = new_session_as('Cliffton Player')
randy.goes_to_profile_edit
randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
randy.

end

private

module AthleteSessionDSL

  def goes_to(place)
    get place
  end

  def goes_to_login
    get '/auth/login'
    assert_response :success
    assert_template 'auth/login'
  end

  def goes_to_profile_edit
    get '/athletes/profile_edit'
    assert_response :success
    assert_template 'athletes/profile_edit'
  end

  def logs_in_as(athlete)
    @current_user = users(athlete)
    post 'auth/login', :user => {:email => @current_user.email, :password => @current_user.password }
    assert_response :redirect
    follow_redirect!
    assert_template 'athletes/profile'
  end

  def updates_profile_with(contact)
    post 'athletes/update_contact', :contact => contact,
                                    :athlete_id => @current_user.id
    assert_flash_equal 'This profile has been updated with new information', :success
    assert_response :redirect
    follow_redirect!
    assert_template 'athletes/profile'
    contact.each { |k,v| assert_equal v, @current_user.profile.attributes[k.to_s] }
    end
  end

end

def new_session
  open_session do |sess|
    sess.extend(AthleteSessionDSL)
    yield sess if block_given?
  end
end

def new_session_as(person)
  new_session do |sess|
    sess.goes_to_login
    sess.logs_in_as(person)
    yield sess if block_given?
  end
end

end

The Problem

So this is fine. So far it's working great. The problem is that I've set it up so that everything my user does is part of my AthleteSessionDSL module in it's own method. Everything randy does has to be a method within the AthleteSessionDSL module.

I wanted to try some non-modular navigation to give Randy a bit more of a free spirit. I thought I'd see if I could take some of the functionality out of the module and do it directly:


  def test_randy_visits
    randy = new_session_as('Cliffton Player')
    randy.goes_to_profile_edit
    randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
    # trying some direct session navigation outside of the randy session object
    get 'athletes/profile'
    assert_response :success
    assert_template 'athletes/profile'
  end
What happened? Well, since `randy` is really just an instance of a Rails session with some handy methods attached to it I immediately left the session when I stopped using `randy`. My request to get the "athletes/profile" page resulted in a redirect to my login page because that method is authentication protected. Well, what's the solution? Do I have to make a million methods for everything I want Randy to do? The Solution ==== It turns out to be quite simple. The methods we have been using like `assert_response` and `get` (and all the rest) are, in fact, methods of the session. The `open_session` call in the `new_session` module creates a session object with all the standard methods on it. So not only can we group functionality into methods and roll those into included modules - we can also call things directly:

  def test_randy_visits
    randy = new_session_as('Cliffton Player')
    randy.goes_to_profile_edit
    randy.updates_profile_with :first => 'Randy', :last => 'BadAsserson'
    randy.get '/athletes/profile'
    randy.assert_response :success
    randy.assert_template 'athletes/profile'
  end
blog comments powered by Disqus