Back to Repositories

Testing Password Reset Email Implementation in Devise

This test suite validates the password reset email functionality in Devise’s mailer system. It ensures proper email delivery, content formatting, and token generation for password reset instructions.

Test Coverage Overview

The test suite provides comprehensive coverage of password reset email functionality:

  • Email delivery verification
  • Content type validation
  • Sender configuration testing
  • Custom mailer integration
  • Localization support for email subjects
  • Password reset token validation

Implementation Analysis

The testing approach uses ActionMailer::TestCase for email testing, implementing setup and teardown methods to ensure clean test states. It leverages Devise’s mailer configuration system and validates both default and custom mailer implementations.

Key patterns include fixture-based user creation, email delivery verification, and token generation testing.

Technical Details

  • ActionMailer::TestCase for email testing
  • Devise::Mailer configuration
  • I18n integration for translations
  • URL token generation and validation
  • Custom mailer class support
  • HTML content type verification

Best Practices Demonstrated

The test suite exemplifies robust testing practices by isolating mailer configuration, implementing proper setup/teardown cycles, and validating both technical and user-facing aspects of the password reset process.

Notable practices include thorough sender configuration testing, internationalization support, and security token validation.

heartcombo/devise

test/mailers/reset_password_instructions_test.rb

            
# frozen_string_literal: true

require 'test_helper'

class ResetPasswordInstructionsTest < ActionMailer::TestCase
  def setup
    setup_mailer
    Devise.mailer = 'Devise::Mailer'
    Devise.mailer_sender = '[email protected]'
  end

  def teardown
    Devise.mailer = 'Devise::Mailer'
    Devise.mailer_sender = '[email protected]'
  end

  def user
    @user ||= begin
      user = create_user
      user.send_reset_password_instructions
      user
    end
  end

  def mail
    @mail ||= begin
      user
      ActionMailer::Base.deliveries.last
    end
  end

  test 'email sent after resetting the user password' do
    assert_not_nil mail
  end

  test 'content type should be set to html' do
    assert_includes mail.content_type, 'text/html'
  end

  test 'send confirmation instructions to the user email' do
    assert_equal [user.email], mail.to
  end

  test 'set up sender from configuration' do
    assert_equal ['[email protected]'], mail.from
  end

  test 'set up sender from custom mailer defaults' do
    Devise.mailer = 'Users::Mailer'
    assert_equal ['[email protected]'], mail.from
  end

  test 'set up sender from custom mailer defaults with proc' do
    Devise.mailer = 'Users::FromProcMailer'
    assert_equal ['[email protected]'], mail.from
  end

  test 'custom mailer renders parent mailer template' do
    Devise.mailer = 'Users::Mailer'
    assert_present mail.body.encoded
  end

  test 'set up reply to as copy from sender' do
    assert_equal ['[email protected]'], mail.reply_to
  end

  test 'set up subject from I18n' do
    store_translations :en, devise: { mailer: { reset_password_instructions: { subject: 'Reset instructions' } } } do
      assert_equal 'Reset instructions', mail.subject
    end
  end

  test 'subject namespaced by model' do
    store_translations :en, devise: { mailer: { reset_password_instructions: { user_subject: 'User Reset Instructions' } } } do
      assert_equal 'User Reset Instructions', mail.subject
    end
  end

  test 'body should have user info' do
    assert_match user.email, mail.body.encoded
  end

  test 'body should have link to confirm the account' do
    host, port = ActionMailer::Base.default_url_options.values_at :host, :port

    if mail.body.encoded =~ %r{<a href=\"http://#{host}:#{port}/users/password/edit\?reset_password_token=([^"]+)">}
      assert_equal user.reset_password_token, Devise.token_generator.digest(user.class, :reset_password_token, $1)
    else
      flunk "expected reset password url regex to match"
    end
  end

  test 'mailer sender accepts a proc' do
    swap Devise, mailer_sender: proc { "[email protected]" } do
      assert_equal ['[email protected]'], mail.from
    end
  end
end