- Hey guys, i need help, am new to testing and am testing my mailer but i do not understand y am getting this error when i run my tests.
- 'expected
ActionMailer::Base.deliveries.sizeto have changed by 1, but was changed by 2' - The emails are triggered by the
after_createhook - My code is working as expected but the tests are failing. Anyone with help is appreciated.
verfiy_mailer.rb
# frozen_string_literal: true
class VerifyMailer < ApplicationMailer
def verification_email(user)
@user = user
mail(to: user.email, subject: 'verification code')
end
end
verify_mailer_spec.rb
# frozen_string_literal: true
require 'rails_helper'
# include ActiveJob::TestHelper
RSpec.describe VerifyMailer, type: :mailer do
let(:user) { create(:user) }
let(:mail) { VerifyMailer.verification_email(user).deliver_now }
it 'renders the receiver email' do
expect(mail.to).to eq([user.email])
end
it 'renders the subject' do
expect(mail.subject).to eq('verification code')
end
it 'renders the sender email' do
expect(mail.from).to eq(['[email protected]'])
end
end
user.rb
# frozen_string_literal: true
class User < ApplicationRecord
after_create :send_user_otp
attr_writer :login
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable ,
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, authentication_keys: [:username]
has_many :payments
validates :email, :username, presence: true, uniqueness: true
validates :username, format: { with: /\A[a-zA-Z0-9_.]*\z/,
message: ' only allow letter, number, underscore and punctuation marks' }
# Loggin with user_name
def login
@login || username
end
def send_user_otp
unverify!
otp = generate_codes
update_column(:otp_code, otp)
VerifyMailer.verification_email(self).deliver_now
touch(:otp_sent_at)
end
def unverify!
update_column(:verified, false)
end
def generate_codes
loop do
code = rand(0o00000..999_999).to_s
break code unless code.length != 6
end
end
end
I have added config.action_mailer.delivery_method = :test to enviroment/test.rb
am also using factory_bot_rails to create user
FactoryBot.define do
factory :user do
sequence(:email) { |n| "person#{n}@example.com" }
sequence(:username) { |n| "user#{n}" }
password { '!Mutebi2' }
password_confirmation { '!Mutebi2' }
end
end
As in most cases, the computer is doing exactly what you told it to do:
Creating the
userwithFactoryBottriggers any/all callbacks:So
create(:user)calls:send_user_otpafter it's created.Then you send another email with
VerifyMailer.verification_email(user).deliver_nowHence why
ActionMailer::Base.deliveries.sizechanges by 2.In your
VerifyMailertests, you are testing the attributes of the email, so you don't need the user to actually be saved or the email to actually be sent.If you need an instance of the email, just remove
deliver_now. And you canbuildinstead ofcreatetheuser:In your
Usertests, you can verify that creating a user also creates the email:Note that I'm not actually testing
ActionMailer::Base.deliveries.sizeat all. That's a service or request test, which should be based on a full implementation, e.g.:When this form is filled out:
Your
Usermodel isn't responsible forVerifyMailer,ApplicationMailerand any other code that handlesdelver_now, it's only responsible for callingVerifyMailerand handing it theuserinstance.And
VerifyMailerisn't responsible for handling delivery, it creates the email and passes it off toActionMailer.Keep your tests focused on the responsibilities of your code, it will make them less brittle.
If you change something in your
ActionMailersetup, you don't wantUsertests to start to fail.