Functional tests with login in Rails

My assignment this week, as senior Ruby on Rails developer for TrexGlobal (an US startup with some cool online apps for real estate investors that help you save lots of dollars on your taxes) was to add tests for some of the controllers in place.

Yes, I know, we should have used TDD(Test Driven Development) and write our tests BEFORE or along with our development, but, you see, in practice this kind of things rarely happens, there are always time constraints and last minute refactorings. Plus, I didn’t have much rails testing experience before this; but it’s never too late to learn, is it?

The main issue here was that the big book Agile Web Development with Rails (Pragmatic Programmers) covers functional tests well, but not in real-life situations. For instance, what if I want to test some actions that require the user to be logged in?

The way to do it is similar to the one described here:
File: test/test_helper.rb:

def login_as(user)
  @request.session[:user] = user ? users(user).id : nil
end

In the test that requires authentication, we’d just have:
File: test/functional/articles_controller_test.rb:

def test_edit
  login_as(:quentin)
  # the method's action calls and assertions...
end

I don’t really like this: what I want instead is to call the authentication method myself, not just “trick” the system into believing this.

I have a LoginController with a login action; I do the following changes to the code above:

File: test/test_helper.rb:
below the line that says
# Add more helper methods to be used by all tests here…
I added my custom method

fixtures :users
def login(email='foo@bar.com', password='fooblitzky', tsap='tax_depreciate')
  old_controller = @controller
  @controller = LoginController.new
  post :login, :user=>{:email=>email, :password=>password}, :tsap=>tsap
  assert_redirected_to :controller => tsap, :action=>'overview'
  assert_not_nil(session[:user_id])
  @controller = old_controller
end

What this method does is:
it saves the current controller in a local variable, changes the context to the LoginController, calls using POST the authentication method and tests if the outcome is ok by means of assertions. Afterwards it switches back the context to the initial one, making sure that any outside tests that call this method won’t get messed up.

So now, in my functional test DepriciationAttributeControllerTest, I can have, for instance, this:

def test_setup_assets_not_empty_properties
   login()
   post :setup_assets
   properties = assigns(:properties)
   assert_not_nil properties
   assert_template 'setup_assets'
end

Simple, ain’t it?
Not really much to see, just a bit of tweaking, saving stuff and restoring it. But it caused a bit of headaches the days before and I hope will save you from some.
Now… let’s get the functional testing started!


Similar Posts:

Leave a Reply