Back to Repositories

Testing SessionsController Authentication Workflow in Devise

This test suite validates the Devise SessionsController functionality, focusing on authentication workflows and session management in a Ruby on Rails application. The tests cover critical user session operations including login attempts, scoped views, and return URL handling.

Test Coverage Overview

The test suite provides comprehensive coverage of the SessionsController functionality:

  • Authentication failure handling and parameter validation
  • Scoped view rendering verification
  • Session return URL management for different request formats
  • Flash message handling for non-navigational requests
  • Mass assignment protection testing

Implementation Analysis

The testing approach employs Devise’s ControllerTestCase framework with ControllerHelpers integration. Tests utilize Rails’ request/response cycle simulation and session manipulation to verify controller behavior. The implementation demonstrates proper handling of different request formats (HTML/JSON) and authentication scenarios.

Technical Details

Testing tools and configuration:

  • Devise::ControllerTestCase as the base test class
  • Devise::Test::ControllerHelpers for authentication support
  • ActiveSupport::Notifications for parameter validation
  • Custom test helper methods for user creation
  • Environment configuration for Devise mappings

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolation of test scenarios using proper setup and teardown
  • Comprehensive edge case coverage including format-specific behavior
  • Proper cleanup of subscribed notifications
  • Clear test naming conventions and organization
  • Effective use of assertions to validate expected behavior

heartcombo/devise

test/controllers/sessions_controller_test.rb

            
# frozen_string_literal: true

require 'test_helper'

class SessionsControllerTest < Devise::ControllerTestCase
  tests Devise::SessionsController
  include Devise::Test::ControllerHelpers

  test "#create doesn't raise unpermitted params when sign in fails" do
    begin
      subscriber = ActiveSupport::Notifications.subscribe %r{unpermitted_parameters} do |name, start, finish, id, payload|
        flunk "Unpermitted params: #{payload}"
      end
      request.env["devise.mapping"] = Devise.mappings[:user]
      request.session["user_return_to"] = 'foo.bar'
      create_user
      post :create, params: { user: {
          email: "[email protected]",
          password: "wrongpassword"
        }
      }
      assert_equal 200, @response.status
    ensure
      ActiveSupport::Notifications.unsubscribe(subscriber)
    end
  end

  test "#create works even with scoped views" do
    swap Devise, scoped_views: true do
      request.env["devise.mapping"] = Devise.mappings[:user]
      post :create
      assert_equal 200, @response.status
      assert_template "users/sessions/new"
    end
  end

  test "#create delete the url stored in the session if the requested format is navigational" do
    request.env["devise.mapping"] = Devise.mappings[:user]
    request.session["user_return_to"] = 'foo.bar'

    user = create_user
    user.confirm
    post :create, params: { user: {
        email: user.email,
        password: user.password
      }
    }
    assert_nil request.session["user_return_to"]
  end

  test "#create doesn't delete the url stored in the session if the requested format is not navigational" do
    request.env["devise.mapping"] = Devise.mappings[:user]
    request.session["user_return_to"] = 'foo.bar'

    user = create_user
    user.confirm
    post :create, params: { format: 'json', user: {
        email: user.email,
        password: user.password
      }
    }

    assert_equal 'foo.bar', request.session["user_return_to"]
  end

  test "#create doesn't raise exception after Warden authentication fails when TestHelpers included" do
    request.env["devise.mapping"] = Devise.mappings[:user]
    post :create, params: { user: {
        email: "[email protected]",
        password: "wevdude"
      }
    }
    assert_equal 200, @response.status
    assert_template "devise/sessions/new"
  end

  test "#destroy doesn't set the flash if the requested format is not navigational" do
    request.env["devise.mapping"] = Devise.mappings[:user]
    user = create_user
    user.confirm
    post :create, params: { format: 'json', user: {
        email: user.email,
        password: user.password
      }
    }
    delete :destroy, format: 'json'
    assert flash[:notice].blank?, "flash[:notice] should be blank, not #{flash[:notice].inspect}"
    assert_equal 204, @response.status
  end

  if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:mass_assignment_sanitizer)
    test "#new doesn't raise mass-assignment exception even if sign-in key is attr_protected" do
      request.env["devise.mapping"] = Devise.mappings[:user]

      ActiveRecord::Base.mass_assignment_sanitizer = :strict
      User.class_eval { attr_protected :email }

      begin
        assert_nothing_raised do
          get :new, user: { email: "allez viens!" }
        end
      ensure
        ActiveRecord::Base.mass_assignment_sanitizer = :logger
        User.class_eval { attr_accessible :email }
      end
    end
  end
end