Back to Repositories

Testing Password Reset Controller Behavior in Devise

This test suite validates the password reset functionality in Devise’s PasswordsController, focusing on redirect behavior and authentication callbacks. It ensures proper handling of password resets and subsequent user authentication flow.

Test Coverage Overview

The test suite comprehensively covers password reset functionality with specific focus on post-reset redirections and authentication workflows. Key areas tested include:

  • Default redirect behavior after password reset
  • Custom redirect path overrides
  • Authentication callback execution
  • Integration with Devise’s core authentication system

Implementation Analysis

The testing approach utilizes Devise’s ControllerTestCase framework with Minitest assertions. It implements controller-level testing patterns with mock expectations and stub methods to isolate functionality. The suite leverages Devise::Test::ControllerHelpers for authentication context and request environment setup.

Technical Details

  • Testing Framework: Minitest
  • Controller Helpers: Devise::Test::ControllerHelpers
  • Mocking Library: Mocha (for expectations and stubs)
  • Environment Setup: Devise.mappings and request.env configuration
  • HTTP Method Testing: PUT requests for password updates

Best Practices Demonstrated

The test suite exemplifies several testing best practices including proper test isolation, clear setup methods, and focused test cases. Each test validates a specific aspect of the password reset workflow with clear assertions and minimal overlap. The use of helper methods (put_update_with_params) promotes DRY principles and maintainable test code.

heartcombo/devise

test/controllers/passwords_controller_test.rb

            
# frozen_string_literal: true

require 'test_helper'

class PasswordsControllerTest < Devise::ControllerTestCase
  tests Devise::PasswordsController
  include Devise::Test::ControllerHelpers

  setup do
    request.env["devise.mapping"] = Devise.mappings[:user]
    @user = create_user.tap(&:confirm)
    @raw  = @user.send_reset_password_instructions
  end

  def put_update_with_params
    put :update, params: { "user" => {
        "reset_password_token" => @raw, "password" => "1234567", "password_confirmation" => "1234567"
      }
    }
  end

  test 'redirect to after_sign_in_path_for if after_resetting_password_path_for is not overridden' do
    put_update_with_params
    assert_redirected_to "http://test.host/"
  end

  test 'redirect accordingly if after_resetting_password_path_for is overridden' do
    custom_path = "http://custom.path/"
    Devise::PasswordsController.any_instance.stubs(:after_resetting_password_path_for).with(@user).returns(custom_path)

    put_update_with_params
    assert_redirected_to custom_path
  end

  test 'calls after_database_authentication callback after sign_in immediately after password update' do
    User.any_instance.expects :after_database_authentication
    put_update_with_params
  end
end