4

I'm writing controller tests in Rails and RSpec, and it seems from reading the source code of ActionController::TestCase that it's not possible to pass arbitrary query parameters to the controller -- only routing parameters.

To work around this limitation, I am currently using with_routing:

with_routing do |routes| # this nonsense is necessary because # Rails controller testing does not # pass on query params, only routing params routes.draw do get '/users/confirmation/:confirmation_token' => 'user_confirmations#show' root :to => 'root#index' end get :show, 'confirmation_token' => CONFIRMATION_TOKEN end 

As you may be able to guess, I am testing a custom Confirmations controller for Devise. This means I am jacking into an existing API and do not have the option to change how the real mapping in config/routes.rb is done.

Is there a neater way to do this? A supported way for get to pass query parameters?


EDIT: There is something else going on. I created a minimal example in https://github.com/clacke/so_13866283 :

spec/controllers/receive_query_param_controller_spec.rb

describe ReceiveQueryParamController do describe '#please' do it 'receives query param, sets @my_param' do get :please, :my_param => 'test_value' assigns(:my_param).should eq 'test_value' end end end 

app/controllers/receive_query_param_controller.rb

class ReceiveQueryParamController < ApplicationController def please @my_param = params[:my_param] end end 

config/routes.rb

So13866283::Application.routes.draw do get '/receive_query_param/please' => 'receive_query_param#please' end 

This test passes, so I suppose it is Devise that does something funky with the routing.


EDIT:

Pinned down where in Devise routes are defined, and updated my example app to match it.

So13866283::Application.routes.draw do resource :receive_query_param, :only => [:show], :controller => "receive_query_param" end 

... and spec and controller updated accordingly to use #show. The test still passes, i.e. params[:my_param] is populated by get :show, :my_param => 'blah'. So, still a mystery why this does not happen in my real app.

3

3 Answers 3

3

Controller tests don't route. You are unit-testing the controller--routing is outside its scope.

A typical controller spec example tests an action:

describe MyController do it "is successful" do get :index response.status.should == 200 end end 

You set up the test context by passing parameters to get, e.g.:

 get :show, :id => 1 

You can pass query parameters in that hash.

If you do want to test routing, you can write routing specs, or request (integration) specs.

Sign up to request clarification or add additional context in comments.

7 Comments

I am testing the controller, not the routing. Unfortunately Rails is not letting me send the parameters I want, because it is entangling routing and controller testing. That's why I need the with_routing.
I think I see what's going on here. You've defined a resource that requires an id in the params hash, but you're not passing it in the spec. You should be able to simply pass both the id and any additional query params in that hash as long as the id is present. viz: { :id => "1", :hello => "world" }
No, the existing routing does not require an :id and there is no error message. get simply does not pass any parameters I give it, unless those parameters are in the routing path.
Are you sure get :show does not require an :id? How is the route defined?
rake routes CONTROLLER=user_confirmations gives me GET /users/confirmation(.:format) user_confirmations#show
|
1

Are you sure there isn't something else going on? I have a Rails 3.0.x project and am passing parameters.. well.. this is a post.. maybe it's different for get, but that seems odd..

before { post :contact_us, :contact_us => {:email => '[email protected]', :category => 'Category', :subject => 'Subject', :message => 'Message'} } 

The above is definitely being used in my controller in the params object.

2 Comments

Yes, I believe a post is handled differently. The code is simply biased against get query parameters, the "rails way" is to pass things as part of the path, apparently. I will check how Devise tests itself, maybe there are clues there.
Oh, Devise only does integration testing for this controller, so then this issue does not show up.
1

I am doing this now:

@request.env['QUERY_STRING'] = "confirmation_token=" # otherwise it's ignored get :show, :confirmation_token => CONFIRMATION_TOKEN 

... but it looks hacky.

If someone could show me a neat and official way to do this, I would be delighted. Judging from what I've seen in the source code of #get and everything it calls, there doesn't seem to be any other way, but I'm hoping I overlooked something.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.