Back to Repositories

Testing Email Change Notifications in Devise Authentication Framework

This test suite validates email change notifications in the Devise authentication framework, focusing on mailer functionality and email content verification. The tests ensure proper handling of email updates for both immediate changes and those requiring reconfirmation.

Test Coverage Overview

The test suite provides comprehensive coverage of email change notification functionality in Devise:

  • Email delivery verification for address changes
  • Content type validation and HTML formatting
  • Sender and reply-to configuration testing
  • Subject line localization testing
  • Email content verification for both direct changes and reconfirmation workflows

Implementation Analysis

The testing approach utilizes ActionMailer::TestCase for mailer functionality verification. It implements setup and teardown methods to ensure consistent test environments, and uses fixture data through create_user and create_admin helpers. The tests leverage Ruby’s tap method for efficient object manipulation and assertion methods for validation.

Technical Details

Key technical components include:

  • ActionMailer::TestCase framework
  • Devise mailer configuration
  • I18n integration for subject lines
  • Custom mailer class support
  • Email delivery tracking through ActionMailer::Base.deliveries

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test environments through setup/teardown methods
  • Comprehensive edge case coverage
  • Clear test naming conventions
  • Efficient test helper methods
  • Proper separation of concerns between different notification scenarios

heartcombo/devise

test/mailers/email_changed_test.rb

            
# frozen_string_literal: true

require 'test_helper'

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

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

  def user
    @user ||= create_user.tap { |u|
      @original_user_email = u.email
      u.update!(email: '[email protected]')
    }
  end

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

  test 'email sent after changing the user email' 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 email changed to the original user email' do
    mail
    assert_equal [@original_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 reply to as different if set in defaults' do
    Devise.mailer = 'Users::ReplyToMailer'
    assert_equal ['[email protected]'], mail.from
    assert_equal ['[email protected]'], mail.reply_to
  end

  test 'set up subject from I18n' do
    store_translations :en, devise: { mailer: { email_changed: { subject: 'Email Has Changed' } } } do
      assert_equal 'Email Has Changed', mail.subject
    end
  end

  test 'subject namespaced by model' do
    store_translations :en, devise: { mailer: { email_changed: { user_subject: 'User Email Has Changed' } } } do
      assert_equal 'User Email Has Changed', mail.subject
    end
  end

  test 'body should have user info' do
    body = mail.body.encoded
    assert_match "Hello #{@original_user_email}", body
    assert_match "has been changed to #{user.email}", body
  end
end

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

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

  def admin
    @admin ||= create_admin.tap { |u|
      @original_admin_email = u.email
      u.update!(email: '[email protected]')
    }
  end

  def mail
    @mail ||= begin
      admin
      ActionMailer::Base.deliveries[-2]
    end
  end

  test 'send email changed to the original user email' do
    mail
    assert_equal [@original_admin_email], mail.to
  end

  test 'body should have unconfirmed user info' do
    body = mail.body.encoded
    assert_match admin.email, body
    assert_match "is being changed to #{admin.unconfirmed_email}", body
  end
end