How to stub Mailer delivery in RSPEC
Asked Answered
G

3

6

I want to stub sending email and return sample email result for further process.

Given I have:

message = GenericMailer.send_notification(id).deliver!

I want to do something like:

allow(GenericMailer).to receive_message_chain(:send_notification, :deliver)
   .and_return(mail_result_no_idea_what_is_like)

but above function obviously fails, GenericMailer does not implement: deliver or deliver! as tried.

I want to return some data as i need to test something like (and more):

message.header.select{|h| h.name == "Date"}.try(:first).try(:value).try(:to_datetime).try(:utc)
Guava answered 26/10, 2018 at 5:28 Comment(4)
Change :deliver to :deliver!?Stranglehold
@Stranglehold i tried that before but same error as above.Guava
do you want to return the contents of the email?Hachure
@Hachure yes :)Guava
R
7

GenericMailer.send_notification is returning an object of class ActionMailer::MessageDelivery

Example with rails 5.1.4 and rspec 3.6.0

it 'delivers notification' do
  copy = double()
  expect(GenericMailer).to receive(:send_notification).and_return(copy)
  expect(copy).to receive(:deliver!) # You can use allow instead of expect
  GenericMailer.send_notification.deliver!
end
Roshelle answered 26/10, 2018 at 8:39 Comment(1)
for rails >5.2.x use deliver_now! instead of deliver!Raff
G
1

Finally came up with a solution. Thanks to @LolWalid info.

copy = double()
# recursive_ostruct_even_array is my method to convert hash/array to OpenStruct Obje
message = recursive_ostruct_even_array({
                                         "header" => [{"name" => "Date", "value" => Time.now.utc}],
                                         "to" => ["[email protected]"],
                                         "from" => ["[email protected]"],
                                         "subject" => "For Testing",
                                         "body" => "-"
                                       })
allow(GenericMailer).to receive(:send_notification).and_return(copy)
allow(copy).to receive(:deliver!).and_return(message)
Guava answered 29/10, 2018 at 3:47 Comment(0)
T
0

An even simpler solution is to use Rspec's native matcher, have_enqueued_mail, which expects a block.

Here's one way to use it, copied from Rspec documentation:

expect {
  MyMailer.welcome(user).deliver_later(wait_until: Date.tomorrow.noon)
}.to have_enqueued_mail(MyMailer, :welcome).at(Date.tomorrow.noon)

Alternatively, you can use Rails assertions, for example assert_enqueued_email_with. This works after having called the mail-enqueing code, not using a block.

Here's an example taken straight from Rails API documentation:

  ContactMailer.with(greeting: "Hello").welcome("Cheers", "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, params: { greeting: "Hello" }, args: ["Cheers", "Goodbye"]
Trossachs answered 13/8 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.