Forum: Ruby on Rails Testing ApplicationController with Sessions

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Chris K. (Guest)
on 2007-01-06 18:16
I have a method in my application controller that I would like to test.
The controller looks like this:


class ApplicationController < ActionController::Base

  def redirect_back_or_default(default="/")
    session[:return_to] ? redirect_to_url(session[:return_to]) :
redirect_to(default)
    session[:return_to] = nil
  end

end

(thx Rick :-)

Anyway, I want to create an isolated functional test for the
redirect_back_or_default method.

I created a new functional test (application_controller_test.rb):


require File.dirname(__FILE__) + '/../test_helper'
require 'application'

# Re-raise errors caught by the controller.
class ApplicationController; def rescue_action(e) raise e end; end

class ApplicationControllerTest <Test::Unit::TestCase

  def setup
    @controller = ApplicationController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    @default_url = "/"
  end

  def test_should_redirect_to_default
    @controller.redirect_back_or_default
  end
end


This test fails (yes, I realize that I don't have any assertions yet
:-).  The error I receive is:

  1) Error:
test_should_redirect_to_default(ApplicationControllerTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]

The nil error pertains to the session object.  So, can anyone provide
direction on how to go about testing this method without involving other
controllers?

Any help appreciated.
Stephan W. (Guest)
on 2007-01-06 19:59
> class ApplicationControllerTest <Test::Unit::TestCase
>
>   def setup
>     @controller = ApplicationController.new
>     @request    = ActionController::TestRequest.new
>     @response   = ActionController::TestResponse.new
>     @default_url = "/"
>   end
>
>   def test_should_redirect_to_default
>     @controller.redirect_back_or_default
>   end
> end
>
>
> This test fails (yes, I realize that I don't have any assertions yet
> :-).  The error I receive is:
>
>   1) Error:
> test_should_redirect_to_default(ApplicationControllerTest):
> NoMethodError: You have a nil object when you didn't expect it!
> You might have expected an instance of Array.
> The error occurred while evaluating nil.[]
>
> The nil error pertains to the session object.  So, can anyone provide
> direction on how to go about testing this method without involving other
> controllers?
>
> Any help appreciated.

Don't you want to do

  get :redirect_back_or_default

instead of invoking the method directly on the controller?

Stephan
Chris K. (Guest)
on 2007-01-06 20:10
>> Any help appreciated.
>
> Don't you want to do
>
>   get :redirect_back_or_default
>
> instead of invoking the method directly on the controller?
>
> Stephan

Is a get the proper way to handle this test?  I will never actually make
a direct :get call to this method.  It is just a helper method that I
want all of my controllers to inherit.  I would prefer to build a test
that is in line with how the application will be used, i.e. my
controllers make a method call to redirect_back_or default vs. creating
an http get.


Here is a simple example of how the method would be used:

class UsersController < ApplicationController

  def create
    @user = User.new( params[:user] )

    if @user.save
      redirect_back_or_default
    else
      flash[:notice] = "Could not create user"
    end

  end

end


redirect_back_or_default could be used in many places, so I don't want
to test it the context of every controller that makes the call.
Stephan W. (Guest)
on 2007-01-06 20:38
Chris K. wrote:
>>> Any help appreciated.
>>
>> Don't you want to do
>>
>>   get :redirect_back_or_default
>>
>> instead of invoking the method directly on the controller?
>>
>> Stephan
>
> Is a get the proper way to handle this test?  I will never actually make
> a direct :get call to this method.  It is just a helper method that I
> want all of my controllers to inherit.  I would prefer to build a test
> that is in line with how the application will be used, i.e. my
> controllers make a method call to redirect_back_or default vs. creating
> an http get.
>
>
> Here is a simple example of how the method would be used:
>
> class UsersController < ApplicationController
>
>   def create
>     @user = User.new( params[:user] )
>
>     if @user.save
>       redirect_back_or_default
>     else
>       flash[:notice] = "Could not create user"
>     end
>
>   end
>
> end
>
>
> redirect_back_or_default could be used in many places, so I don't want
> to test it the context of every controller that makes the call.

Ok I was also wondering about that.

If you get a nil, you could initialize the field just for this test
then.

  @controller.instance_eval ...

and set up @session = {}, define session reader and session[] writer
methods


Stephan
Stephan W. (Guest)
on 2007-01-06 20:48
> If you get a nil, you could initialize the field just for this test
> then.
>
>   @controller.instance_eval ...
>
> and set up @session = {}, define session reader and session[] writer
> methods


Yes, like this


@controller.instance_eval "@session = {} "
@controller.instance_eval "def session ; @session ; end "

then you could access the method in question.

Stephan
Chris K. (Guest)
on 2007-01-06 22:42
>> If you get a nil, you could initialize the field just for this test
>> then.
>>
>>   @controller.instance_eval ...
>>
>> and set up @session = {}, define session reader and session[] writer
>> methods
>
>
> Yes, like this
>
>
> @controller.instance_eval "@session = {} "
> @controller.instance_eval "def session ; @session ; end "
>
> then you could access the method in question.
>
> Stephan

Unfortunately, I think the solution may be a bit more involved than
that.  Here is the current test:

  def test_should_redirect_to_default
    @controller.instance_eval "@session = {} "
    @controller.instance_eval "def session ; @session ; end "
    @controller.redirect_back_or_default
    assert_redirected_to "/"
  end

and the result

test_should_redirect_to_default(ApplicationControllerTest):
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.protocol
    ....../config/../vendor/rails/actionpack/lib/action_controller/base.rb:977:in
`redirect_to'

The method in the application controller is actually going to try to do
a redirect:

session[:return_to] ? redirect_to_url(session[:return_to]) :

I don't know enough about the rails http internals yet to understand
exactly where I'm missing the boat.  Is there some more internal
plumbing that needs to be instantiated?
redirect_to(default)
Stephan W. (Guest)
on 2007-01-06 23:02
Chris K. wrote:
>:
>:
> and the result
>
> test_should_redirect_to_default(ApplicationControllerTest):
> NoMethodError: You have a nil object when you didn't expect it!
> The error occurred while evaluating nil.protocol
>     ....../config/../vendor/rails/actionpack/lib/action_controller/base.rb:977:in
> `redirect_to'
>
> The method in the application controller is actually going to try to do
> a redirect:
>
> session[:return_to] ? redirect_to_url(session[:return_to]) :
>
> I don't know enough about the rails http internals yet to understand
> exactly where I'm missing the boat.  Is there some more internal
> plumbing that needs to be instantiated?
> redirect_to(default)

Ok, maybe you can live with redefining the redirect_to_url as well for
your testing @controller instance.

@controller.instance_eval " @redirected_to "
@controller.instance_eval " def redirect_to(a) @redirected_to = a ; end
"
@controller.instance_eval " def redirected_to @redirected_to ; end  "

and probably a little more.

Then you can pick up the result with asserts.

But also, you probably want to test this action as it impacts the
UsersController; meaning how the UsersController acts is what matters,
not
so much what the ApplicationController does.


Stephan
Chris K. (Guest)
on 2007-01-06 23:06
Chris K. wrote:
>>> If you get a nil, you could initialize the field just for this test
>>> then.
>>>
>>>   @controller.instance_eval ...
>>>
>>> and set up @session = {}, define session reader and session[] writer
>>> methods
>>
>>
>> Yes, like this
>>
>>
>> @controller.instance_eval "@session = {} "
>> @controller.instance_eval "def session ; @session ; end "
>>
>> then you could access the method in question.
>>
>> Stephan
>
> Unfortunately, I think the solution may be a bit more involved than
> that.  Here is the current test:
>
>   def test_should_redirect_to_default
>     @controller.instance_eval "@session = {} "
>     @controller.instance_eval "def session ; @session ; end "
>     @controller.redirect_back_or_default
>     assert_redirected_to "/"
>   end
>
> and the result
>
> test_should_redirect_to_default(ApplicationControllerTest):
> NoMethodError: You have a nil object when you didn't expect it!
> The error occurred while evaluating nil.protocol
>     ....../config/../vendor/rails/actionpack/lib/action_controller/base.rb:977:in
> `redirect_to'
>
> The method in the application controller is actually going to try to do
> a redirect:
>
> session[:return_to] ? redirect_to_url(session[:return_to]) :
>
> I don't know enough about the rails http internals yet to understand
> exactly where I'm missing the boat.  Is there some more internal
> plumbing that needs to be instantiated?
> redirect_to(default)

The line in actionpack/lib/action_controller/base.rb (977) throwing the
error is:

redirect_to(request.protocol + request.host_with_port + options)


So, I guess the question is how do I mock up the entire request/response
process and test this method in isolation?
This topic is locked and can not be replied to.