Otherwise known as “Testing Rails Controllers with Complex Parameter Types”
A co-worker of mine and I ran into a Ruby on Rails controller testing issue recently that stumped us for quite awhile. In hindsight, it shouldn’t have.
Here is our story.
Said co-worker is new to Rails and was working through his first functional test – in this case a pretty standard login controller test. He was testing the account creation (add_user) action like this:
def test_create_account
# Emulate account creation request
post :add_user, { :username => 'user', :password => 'pass' }
# Test that successful creation redirects to login
assert_redirect_to :controller => 'login', :action => 'login'
end
Easy enough, yeah? Well yes, except this doesn’t work. I quickly figured out that it was because the controller method was looking for parameters in standard rails format as a hash of the “user” key in the param hash:
def add_user
@user = User.new(params[:user]) #Note the "params[:user]"
if request.post? and @user.save
redirect_to(:action => "login")
end
end
Arguments were being passed in as their simple property names like username but the controller was looking for that property on the collection parameters for the user, hence the params[:user]. Okay, easy enough, we just need to pass in these nested types in our functional test.
Unfortunately, the documentation we were aware of (here and here) always use very simple examples without nested parameters.
So, after going through all of these formats modeled after what the parameters are called in the actual view:- :user_username = ‘user’
- ‘user_username’ = ‘user’
- ‘user[username]’ = ‘user’
I finally admitted defeat and took a peek at some of Typo’s functional tests to figure out what is quite intuitive in hindsight. Because you’re passing in a mock representation of the real HTTP parameters, you don’t want to use the string versions of the parameters but actually set the parameter hash directly. Duh.
So, when your controller is expecting a nested hash keyed off the :user symbol, give it one!
def test_create_account
# Emulate account creation request
post :add_user, { :user =>
{:username => 'user', :password => 'pass' }
}
# Test that successful creation redirects to login
assert_redirect_to :controller => 'login', :action => 'login'
end
Thank you open-source, and Typo specifically.
tags: rubyonrails, functional testing

Thanks. Doesn’t this seem to defeat some of the purposes of these sorts of tests though? I suppose I should just trust the parameter hash code implicitly, but I would really prefer for there to be a 1-1 mapping from my form names to my post request test. Any idea how I’d do that?