Which level to test user authentication?

For testing authentication which confirms the page requires users to login to see the page, do you test it in feature scenario level or controller level? Do you test every feature scenarios or controllers that requires user to login by requesting those pages by a guest user? For example, I use devise for most of my controllers have authenticate_user! in before_filter.

I almost never test these at the feature level. Browsing around in capybara is pretty heavy artillery for checking a redirect.

I start with controller specs. If you stub most things out, there usually isn’t too much red tape between you and checking for an access denied redirect.

In some cases, you can do something more generic, like using a common superclass or middleware, and try testing things more generically.

I totally agree to test on controller level. Another thing if we have one controller like this:

class PostsController < ApplicationController
  before_filter :authenticate_user!

  def index

  def show


So basically, we want to make sure the controller is secure because developers might forget to have :authenticate_user! in before filters. So in this case, so do you usually test each action separately by having an example of trying to invoke that action without log in and expect it redirect to login path? Or you usually don’t care about authentication, you just focus on example of the controller’s functionalities and assume you should already have :authenticate_user! in before filters.

Before filters are tricky, because they aren’t easy to test directly. It’s hard to make sure that you have the filter applied to the correct methods and that the filters are applied in the correct order. There isn’t really a way to test them without invoking a controller action.

An alternative is to have a superclass like AuthenticatedController and subclass from that instead. You can test that the before filter is applied to everything generically: https://www.relishapp.com/rspec/rspec-rails/v/2-14/docs/controller-specs/anonymous-controller

And then you can just check that the controllers use the correct superclass. Unless you have logic to skip the filter for certain actions, there aren’t many ways for actions to slip through the cracks.

If you have resources for which some actions are protected and some aren’t, you can revert to testing the before filter by invoking the protected actions in tests, or you could try splitting up the controllers into protected and unprotected namespaces so that you can still use superclasses where appropriate.

Another alternative to superclasses is middleware, but that requires having the ability to find and authorize models outside of the controller, which requires some forethought.

1 Like

Thank @jferris for pointing out different possible solutions here.