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.
6b0f930e67b1f6341e379b201a477cee?d=identicon&s=25 Chris Kilmer (chriskilmer)
on 2007-01-06 17: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.
72ea925c0ca3d19fdd2f12fa76681624?d=identicon&s=25 Stephan Wehner (stephanwehner)
on 2007-01-06 18: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
6b0f930e67b1f6341e379b201a477cee?d=identicon&s=25 Chris Kilmer (chriskilmer)
on 2007-01-06 19: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.
72ea925c0ca3d19fdd2f12fa76681624?d=identicon&s=25 Stephan Wehner (stephanwehner)
on 2007-01-06 19:38
Chris Kilmer 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
72ea925c0ca3d19fdd2f12fa76681624?d=identicon&s=25 Stephan Wehner (stephanwehner)
on 2007-01-06 19: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
6b0f930e67b1f6341e379b201a477cee?d=identicon&s=25 Chris Kilmer (chriskilmer)
on 2007-01-06 21: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)
72ea925c0ca3d19fdd2f12fa76681624?d=identicon&s=25 Stephan Wehner (stephanwehner)
on 2007-01-06 22:02
Chris Kilmer 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
6b0f930e67b1f6341e379b201a477cee?d=identicon&s=25 Chris Kilmer (chriskilmer)
on 2007-01-06 22:06
Chris Kilmer 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.