Back to Repositories

Validating User Authentication Requirements in Devise Framework

This test suite validates the Devise gem’s user authentication functionality, focusing on email and password validation rules. It ensures proper validation of user credentials and authentication requirements in Rails applications.

Test Coverage Overview

The test suite comprehensively covers email and password validation scenarios.

  • Email validation including format, uniqueness, and presence
  • Password requirements including length constraints and confirmation
  • Edge cases for blank values and invalid formats
  • Integration with ActiveSupport::TestCase framework

Implementation Analysis

The testing approach utilizes Minitest’s assertion-based syntax within Rails’ testing framework. The implementation follows a systematic pattern of validating user authentication requirements, leveraging Devise’s Validatable module features and Rails’ built-in validation mechanisms.

  • Assertion-based test methods
  • Stub usage for password validation scenarios
  • Error message verification

Technical Details

  • Testing Framework: Minitest
  • Test Environment: ActiveSupport::TestCase
  • Dependencies: Devise gem, test_helper
  • Key Classes: ValidatableTest
  • Configuration: UTF-8 encoding, frozen string literals

Best Practices Demonstrated

The test suite exhibits strong testing practices through comprehensive validation coverage and clear test organization.

  • Isolated test cases for each validation requirement
  • Descriptive test method names
  • Thorough edge case coverage
  • Proper error message validation
  • Clean test data setup and teardown

heartcombo/devise

test/models/validatable_test.rb

            
# encoding: UTF-8
# frozen_string_literal: true

require 'test_helper'

class ValidatableTest < ActiveSupport::TestCase
  test 'should require email to be set' do
    user = new_user(email: nil)
    assert user.invalid?
    assert user.errors[:email]
    assert user.errors.added?(:email, :blank)
  end

  test 'should require uniqueness of email if email has changed, allowing blank' do
    existing_user = create_user

    user = new_user(email: '')
    assert user.invalid?
    assert_no_match(/taken/, user.errors[:email].join)

    user.email = existing_user.email
    assert user.invalid?
    assert_match(/taken/, user.errors[:email].join)

    user.save(validate: false)
    assert user.valid?
  end

  test 'should require correct email format if email has changed, allowing blank' do
    user = new_user(email: '')
    assert user.invalid?
    assert_not_equal 'is invalid', user.errors[:email].join

    %w{invalid_email_format 123 $$$ () ☃}.each do |email|
      user.email = email
      assert user.invalid?, "should be invalid with email #{email}"
      assert_equal 'is invalid', user.errors[:email].join
    end

    user.save(validate: false)
    assert user.valid?
  end

  test 'should accept valid emails' do
    %w([email protected] [email protected] [email protected] [email protected] [email protected] 1☃[email protected]).each do |email|
      user = new_user(email: email)
      assert user.valid?, "should be valid with email #{email}"
      assert_blank user.errors[:email]
    end
  end

  test 'should require password to be set when creating a new record' do
    user = new_user(password: '', password_confirmation: '')
    assert user.invalid?
    assert user.errors.added?(:password, :blank)
  end

  test 'should require confirmation to be set when creating a new record' do
    user = new_user(password: 'new_password', password_confirmation: 'blabla')
    assert user.invalid?

    assert user.errors.added?(:password_confirmation, :confirmation, attribute: "Password")
  end

  test 'should require password when updating/resetting password' do
    user = create_user

    user.password = ''
    user.password_confirmation = ''

    assert user.invalid?
    assert user.errors.added?(:password, :blank)
  end

  test 'should require confirmation when updating/resetting password' do
    user = create_user
    user.password_confirmation = 'another_password'
    assert user.invalid?

    assert user.errors.added?(:password_confirmation, :confirmation, attribute: "Password")
  end

  test 'should require a password with minimum of 7 characters' do
    user = new_user(password: '12345', password_confirmation: '12345')
    assert user.invalid?
    assert_equal 'is too short (minimum is 7 characters)', user.errors[:password].join
  end

  test 'should require a password with maximum of 72 characters long' do
    user = new_user(password: 'x'*73, password_confirmation: 'x'*73)
    assert user.invalid?
    assert_equal 'is too long (maximum is 72 characters)', user.errors[:password].join
  end

  test 'should not require password length when it\'s not changed' do
    user = create_user.reload
    user.password = user.password_confirmation = nil
    assert user.valid?

    user.password_confirmation = 'confirmation'
    assert user.invalid?
    assert_not (user.errors[:password].join =~ /is too long/)
  end

  test 'should complain about length even if password is not required' do
    user = new_user(password: 'x'*73, password_confirmation: 'x'*73)
    user.stubs(:password_required?).returns(false)
    assert user.invalid?
    assert_equal 'is too long (maximum is 72 characters)', user.errors[:password].join
  end

  test 'should not be included in objects with invalid API' do
    exception = assert_raise RuntimeError do
      Class.new.send :include, Devise::Models::Validatable
    end

    expected_message = /Could not use :validatable module since .* does not respond to the following methods: validates_presence_of.*/
    assert_match expected_message, exception.message
  end

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