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!
[tags]Rails, test, functional test, authentication, login[/tags]

You saved my sanity. Thanks for posting this!
20/05/2007 @ 12:38 am
You welcome
Feel free to share or email any rails tips you discover while coding ; the Rails community really needs this…
20/05/2007 @ 8:35 am
Nice technique =]
05/07/2007 @ 11:47 am
Nice article, it worked for me.
07/08/2007 @ 3:08 am
This is the biggest feature of RubyonRails. Testing
Thank you so much for this article
16/05/2008 @ 4:36 pm