Back to Repositories

Testing Authenticatable Module Authentication Methods in Devise

This test suite validates the core authentication functionality in Devise’s Authenticatable module. It focuses on user authentication methods, parameter filtering, and error handling mechanisms essential for secure user management.

Test Coverage Overview

The test suite covers essential authentication operations including required fields validation, custom filtering parameters, and user lookup functionality.

  • Tests required_fields validation for User model
  • Verifies custom filtering in find_first_by_auth_conditions
  • Validates case-insensitive and whitespace-stripped email lookups
  • Tests error handling for blank and invalid credentials

Implementation Analysis

The testing approach utilizes ActiveSupport::TestCase for unit testing Devise’s authentication mechanisms. It implements systematic validation of user lookup methods with various parameter configurations and edge cases.

The tests employ Devise’s configuration-aware testing patterns, particularly for email case sensitivity and whitespace handling, while ensuring proper parameter sanitization for ActionController::Parameters.

Technical Details

  • Testing Framework: Minitest with ActiveSupport::TestCase
  • Mocking Library: Mocha (indicated by expects method usage)
  • Test Environment: Rails integration testing
  • Key Classes: User model with Devise authentication
  • Configuration Dependencies: case_insensitive_keys and strip_whitespace_keys

Best Practices Demonstrated

The test suite exemplifies robust testing practices through comprehensive coverage of authentication scenarios.

  • Isolated unit tests for specific functionality
  • Clear test naming conventions
  • Proper setup and teardown of test data
  • Configuration-aware test cases
  • Security-conscious parameter handling validation

heartcombo/devise

test/models/authenticatable_test.rb

            
# frozen_string_literal: true

require 'test_helper'

class AuthenticatableTest < ActiveSupport::TestCase
  test 'required_fields should be an empty array' do
    assert_equal [], Devise::Models::Validatable.required_fields(User)
  end

  test 'find_first_by_auth_conditions allows custom filtering parameters' do
    user = User.create!(email: "[email protected]", password: "1234567")
    assert_equal user, User.find_first_by_auth_conditions({ email: "[email protected]" })
    assert_nil User.find_first_by_auth_conditions({ email: "[email protected]" }, id: user.id.to_s.next)
  end

  # assumes default configuration of
  # config.case_insensitive_keys = [:email]
  # config.strip_whitespace_keys = [:email]
  test 'find_or_initialize_with_errors uses parameter filter on find' do
    user = User.create!(email: "[email protected]", password: "1234567")
    assert_equal user, User.find_or_initialize_with_errors([:email], { email: " [email protected] " })
  end

  # assumes default configuration of
  # config.case_insensitive_keys = [:email]
  # config.strip_whitespace_keys = [:email]
  test 'find_or_initialize_with_errors uses parameter filter on initialize' do
    assert_equal "[email protected]", User.find_or_initialize_with_errors([:email], { email: " [email protected] " }).email
  end

  test 'find_or_initialize_with_errors adds blank error' do
    user_with_error = User.find_or_initialize_with_errors([:email], { email: "" })
    assert user_with_error.errors.added?(:email, :blank)
  end

  test 'find_or_initialize_with_errors adds invalid error' do
    user_with_error = User.find_or_initialize_with_errors([:email], { email: "[email protected]" })
    assert user_with_error.errors.added?(:email, :invalid)
  end

  if defined?(ActionController::Parameters)
    test 'does not passes an ActionController::Parameters to find_first_by_auth_conditions through find_or_initialize_with_errors' do
      user = create_user(email: '[email protected]')
      attributes = ActionController::Parameters.new(email: '[email protected]')

      User.expects(:find_first_by_auth_conditions).with({ 'email' => '[email protected]' }).returns(user)
      User.find_or_initialize_with_errors([:email], attributes)
    end
  end
end