Finding it difficult to test a before_filter on my ApplicationController

Hi All,
I'm writing tests for my new rails app and I've run into a bit of a brick
wall. I'm trying to write a test for my ApplicationController which has a
simple authorize method. And when I try and run the test I'm hitting a nil
object exception. Here's what my ApplicationController looks like:

class ApplicationController < ActionController::Base
  before_filter :authorize, :except => :login
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for
details

  # Scrub sensitive parameters from your log
  # filter_parameter_logging :password
  
  public
    def authorize
      unless User.find_by_id(session[:user_id])
        session[:original_uri] = request.request_uri
        flash[:notice] = "Please log in"
        redirect_to :controller => 'access_control', :action => 'login'
      end
    end
end

I've created a test for the Application Controller and tried to mock out the
controller as much as possible but I don't think I've covered everything.

require 'test_helper'

class ApplicationControllerTest < ActionController::TestCase

  def setup
    @controller = ApplicationController.new
    @request = ActionController::TestRequest.new
    @response = ActionController::TestResponse.new
    @controller.session = ActionController::TestSession.new
  end

require 'test_helper'

class ApplicationControllerTest < ActionController::TestCase

  def setup
    @controller = ApplicationController.new
    @request = ActionController::TestRequest.new
    @response = ActionController::TestResponse.new
    @controller.request = @request
    @controller.response = @response
    @controller.session = ActionController::TestSession.new
  end

def test_authorize_works_when_user_logged_in
   #setup
   logged_in

   @request.request_uri = "http://test.host/"
   
   #exercise
   ApplicationController.new.authorize
   
   #assertions
   assert_equals @request_uri = "http://test.host/"
end
end

When I run my tests this is what I get :

1) Error:
test_authorize_works_when_user_logged_in(ApplicationControllerTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
    app/controllers/application_controller.rb:14:in `authorize'
    functional/application_controller_test.rb:21:in
`test_authorize_works_when_user_logged_in'

Its failing on the line where I try to look up the user from the session
object.

Is there a better way to mock out the ApplicationController? Why doesn't
rails generate a test in the first place for the application controller am I
barking up the wrong tree? I have execised this code from another
controller test but it doesn't feel right to have to use another controller
to test this, and in the future I can see it being awkward when I want to
test locale or cookie functionality.

I've had a good search for this and found a lot of questions relating to
this but no good answers which leads me to believe that I'm missing an
obvious convention of rails around not testing the application controller.

Any help or advise would be appreciated.

In my opinion this test belongs to the UserSession or User controller
cause you're testing if a user is authorized not the application. You
just need to put it into the application controller for its
application-wide accessibility. Besides that I can't find anything wrong
in the authorize function. Put it in an user test and it should work.

Marnen Laibow-Koser-2 wrote:

In my opinion this test belongs to the UserSession or User controller
cause you're testing if a user is authorized not the application. You
just need to put it into the application controller for its
application-wide accessibility. Besides that I can't find anything wrong
in the authorize function. Put it in an user test and it should work.

I'm not sure if I agree with you on that point. I have a user model and an
AccessControlController (terrible name I know) which handles the
login/logout actions, and the user model handles encrypting the password and
comparing a password against what's in the database.

What I'm doing in this method is merely checking if the user_id is present
in the session and if that's a valid user id. If it is I do nothing, if it
isn't I retain the uri they were trying to hit and redirect to the login
page.

This is application wide behavior and I feel its correct as far as I
understand the rails pattern to keep it in the application_controller.

Whether this is the right or wrong place for it, is it possible to test the
ApplicationController I should I just never put code into it?

Of course, you can keep it in the ApplicatonController. That's what the
ApplicationController is for. I just wouldn't test the
ApplicationController itself cause it doesn't make sense to me and I
guess you're not supposed to test it anyway since there's no
application_test.rb or anything being created when you create a Rails
application :slight_smile:
I'd move the tests you have right now in your ApplicationController test
file to the UserController test or wherever you think it belongs to and
you should be fine.

Marnen Laibow-Koser-2 wrote:

Of course, you can keep it in the ApplicatonController. That's what the
ApplicationController is for. I just wouldn't test the
ApplicationController itself cause it doesn't make sense to me and I
guess you're not supposed to test it anyway since there's no
application_test.rb or anything being created when you create a Rails
application :slight_smile:
I'd move the tests you have right now in your ApplicationController test
file to the UserController test or wherever you think it belongs to and
you should be fine.

Yeah I guess that works, I've already exercised this code elsewhere its just
seems cleaner to me to have a test as close to the code its testing as
possible. So when it eventually breaks in a few months time and I've
forgotten all about the fact that I couldn't test in the
ApplicationController I wont be looking in the UserController for the
problem. I guess I can just drop a comment in stating that.

Anyone know if it was a deliberate design decision not to test the
ApplicationController or if there was a reason it couldn't be done? Or even
better if there's a way to test it?

Anyone know if it was a deliberate design decision not to test the
ApplicationController or if there was a reason it couldn't be done? Or
even
better if there's a way to test it?

I do not know about any design decisions but I do agree with Cojones to
move test_authorize_works_when_user_logged_in into the User or
AccessControl test.

Marnen Laibow-Koser-2 wrote:

I do not know about any design decisions but I do agree with Cojones to
move test_authorize_works_when_user_logged_in into the User or
AccessControl test.

I don't mean to be pedantic when asking this, only want to have a better
understanding of the situation. But why do you feel that way? The
functionality belongs in the ApplicationController so surely it follows that
any tests on that functionality should be in the ApplicationControllerTest.

Assuming for a moment (and I don't know if this is true) that you
can't test application controller directly, there's no reason you
cannot create a subclass of ApplicationController that only exists in
your test and which you use to exercise the methods provided by
ApplicationController.

Fred

Frederick Cheung-2 wrote:

Assuming for a moment (and I don't know if this is true) that you
can't test application controller directly, there's no reason you
cannot create a subclass of ApplicationController that only exists in
your test and which you use to exercise the methods provided by
ApplicationController.

Fred

That's a fair point, but it involves mocking out the ApplicationController
and everything it relies on, that's what the rails framework is there for,
just curious why I can't seem to access it.

Frederick Cheung wrote:

Anyone know if it was a deliberate design decision not to test the
ApplicationController or if there was a reason it couldn't be done? �Or even
better if there's a way to test it?

Assuming for a moment (and I don't know if this is true) that you
can't test application controller directly, there's no reason you
cannot create a subclass of ApplicationController that only exists in
your test and which you use to exercise the methods provided by
ApplicationController.

As you've discovered, controller tests are painful. Forget about them
and use Cucumber features instead. You'll be a lot happier.

Fred

Best,

That's not quite what I meant. What i meant was

class ApplicationControllerTest < ActionController::TestCase
  class StubController < ApplicationController
    def index
       render :text => 'index action'
    end
  end
  tests ApplicationControllerTest::StubController

  test "non logged in user should be redirected to login page" do
    get :index
    assert_redirected_to '/login'
  end
end

No mocking involved!

Fred

Frederick Cheung-2 wrote:

That's not quite what I meant. What i meant was

class ApplicationControllerTest < ActionController::TestCase
  class StubController < ApplicationController
    def index
       render :text => 'index action'
    end
  end
  tests ApplicationControllerTest::StubController

  test "non logged in user should be redirected to login page" do
    get :index
    assert_redirected_to '/login'
  end
end

No mocking involved!

Fred

Yeah no mocking involved if I'm testing a method with nothing in it but I'm
testing a method with calls to the session object, request object, flash
object and redirect_to