How to test instance variables in an ActionMailer object? - ruby-on-rails

There is a before_action callback method in my ActionMailer object which is responsible for setting some instance variables.
class TestMailer < ApplicationMailer
before_action :set_params
def send_test_mail
mail(to: #email, subject: subject)
end
def set_params
#account = account.email
#date = some_action(account.updated_at)
end
end
The question is How one can test these variables in a rspec test?
some thing like:
describe TestMailer do
describe '#set_params' do
described_class.with(account: account, subject: subject).send_test_mail.deliver_now
expect(#date).to eq(Date.today)
end
end
any clue would be highly appreciated.

I think that instead of testing the instance variables, it would be better to test the email body, for example:
expect(mail.body.encoded).to include(account.updated_at)

you could setup a spy inside a mock method instance_variable_set, then validate that spy
class TestMailer < ApplicationMailer
attr_accessor :day
# ...
end
describe TestMailer do
let(:freeze_today) { Time.now.utc }
it '#set_params' do
# freeze today
allow_any_instance_of(TestMailer).to receive(:some_action)
.with(account.updated_at)
.and_return(freeze_today)
# spy
#spy = nil
allow_any_instance_of(TestMailer).to receive(:day=) do |time|
#spy = time
end
described_class.with(account: account, subject: subject)
.send_test_mail
.deliver_now
expect(#spy).to eq(freeze_today)
# or just simple like this
expect_any_instance_of(TestMailer).to receive(:day=).with(freeze_today)
end
end

Related

Sending multiple mails from a single method in ActionMailer

I have a simple mailer
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def inform_everyone
inform_teacher.deliver
inform_division.deliver
end
end
Calling inform_teacher and inform_division everything works well. But when I try to call inform_everyone just one blank email arrives.
Is it possible to combine multiple email method though one method?
Found solution to this:
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def self.inform_everyone
ApplyMailer.inform_teacher.deliver
ApplyMailer.inform_division.deliver
end
end

Rails4: Cannot send email from lib/module

I have this weird thing going on in my rails4 app:
I created event.rb in the lib folder.
In there, I call a mailer:
def whatever
puts 'here'
UserMailer.welcome(user)
puts 'there'
end
which is calling
class UserMailer < ActionMailer::Base
def welcome(user)
#user = user
mail(to: #user.mailer, subject: 'Welcome to my app').deliver
end
end
The weird thing is that the method welcome is never called, while whatever is called, without raising any error (the logs are there).
But if I call UserMailer.welcome(User.first) in the console, it is sent.
What am I doing wrong? Is it that it is not possible to send an email from a module? I should move this code to a model? That would be weird.
Thanks in advance
IMO mailer should look like this:
class UserMailer < ActionMailer::Base
def welcome(user)
#user = user
mail(to: #user.mailer, subject: 'Welcome to my app') #.deliver removed
end
end
and should be invoked with this manner:
def whatever
puts 'here'
UserMailer.welcome(user).deliver_now # and added here
puts 'there'
end

How to move mailer inside namespace

I've got a Rails 3 mailer that works fine.
class Notifier < ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
The view for this will render the #user instance variable correctly and the email is sent without any problem.
However, when I namespace the mailer, everything breaks. With the mailer structured like this.
class Foo::Notifier < ::ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
And the view inside app/view/foo, Rails is unable to find the html template. The email sends, but there is nothing inside the body.
What am I doing wrong?
The views should be stored in app/view/foo/notifier, specifically app/view/foo/notifier/cool_email.EXTENSION.
FYI, it's always a good practice to append Mailer to the name of a mailer.
class Foo::NotifierMailer < ::ActionMailer::Base
or
class Foo::NotificationMailer < ::ActionMailer::Base
It prevents conflicts and makes possible to immediately understand the scope of the class.

How To Tell The Difference Between Ruby Class and Instance Methods

Here is some code in a recent Railscast:
class UserMailer < ActionMailer::Base
default from: "from#example.com"
def password_reset(user)
#user = user
mail :to => user.email, :subject => "Password Reset"
end
end
and this is in a controller
def create
user = User.find_by_email(params[:email])
UserMailer.password_reset(user).deliver
redirect_to :root, :notice => "Email sent with password reset instructions."
end
The password_reset method looks like an instance method to me, yet it looks like it's being called like a class method. Is it an instance or a class method, or is there something special about this UserMailer class?
Looking in the source (https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb), Rails uses method_missing to create a new instance of the ActionMailer. Here's the relevant part from the source:
def method_missing(method_name, *args) # :nodoc:
if respond_to?(method_name)
new(method_name, *args).message
else
super
end
end

Override ActionMailer subject in staging environment

I would like all the emails sent from our staging server to have the phrase "[STAGING] " prefaced in the subject. Is there an elegant way to do this in Rails 3.2 using ActionMailer?
Here's an elegant solution I found using ActionMailer Interceptor based on an existing answer.
# config/initializers/change_staging_email_subject.rb
if Rails.env.staging?
class ChangeStagingEmailSubject
def self.delivering_email(mail)
mail.subject = "[STAGING] " + mail.subject
end
end
ActionMailer::Base.register_interceptor(ChangeStagingEmailSubject)
end
This works for Rails 4.x
class UserMailer < ActionMailer::Base
after_action do
mail.subject.prepend('[Staging] ') if Rails.env.staging?
end
(...)
end
Not really, inheritance is about as elegant as it gets.
class OurNewMailer < ActionMailer::Base
default :from => 'no-reply#example.com',
:return_path => 'system#example.com'
def subjectify subject
return "[STAGING] #{subject}" if Rails.env.staging?
subject
end
end
Then you can inherit from each of your mailers.
# modified from the rails guides
class Notifier < OurNewMailer
def welcome(recipient)
#account = recipient
mail(:to => recipient.email_address_with_name, :subject => subjectify("Important Message"))
end
end
I don't think this is as clean as what you were hopeing for, but this will dry it up a bit.

Resources