# How to Mock ActionMailer

The Rails `ActionMailer` is a pretty useful tool. 

Unfortunately, it's extremely awkward to mock for tests.

Since these emails are critical to our business I spent some time looking at how to write clean test to make sure my mailers are called when they should be. 

## The Business Case

So we're working on a Point of Sale for the the [Frosty Treat](https://www.frostytreat.ca/) in Kengsington PEI. The CEO is all about data and dopamine rushes, so they want to get an email *every time* a banana gets split...for whatever reason. 

Here's what our test should roughly look like:

**spec/banana_spec.rb**
```ruby
it 'sends an :banana_split email after being #split' do
  banana = Banana.new

  # expect BananaMailer to send a :banana_split email later

  banana.split
end
```

## Mocking out the BananaMailer

The ActionMailer API is nice to work with...but involves a lot of weird objects. After a bit of fiddling around and searching through [StackOverflow](https://stackoverflow.com/questions/45207926/can-not-test-mailer-action-called-in-rspec-rails) we find the magic spell we need:

**spec/banana_spec.rb**
```ruby
it 'sends an :banana_split email after being #split' do
  mailer = double(:mailer)
  mail = double(:mail)

  expect(EmployeeOnboardingMailer).to receive(:with).and_return(mailer)
  expect(mailer).to receive(expected_method).and_return(mail)
  expect(mail).to receive(:deliver_later)

  banana = FactoryBot.create(:banana)

  banana.split
end
```

Great! It works. 

It's a mess though, and the thought of writing that every time I need to test a mailer is enough to keep me from ever writing a mailer test again.

Luckily there's a way around this.

## Custom RSpec matchers to the rescue

RSpec makes it easy to create [custom matchers](https://relishapp.com/rspec/rspec-expectations/v/2-3/docs/custom-matchers/define-matcher) for your tests.

Let's build a super simple example:

**spec/action_mailer_helper.rb**
```ruby
RSpec::Matchers.define :deliver_later do |expected_method|
  match do |mailer_class|
    mailer = double(:mailer)
    mail = double(:mail)

    expect(mailer_class).to receive(:with).and_return(mailer)
    expect(mailer).to receive(expected_method).and_return(mail)
    expect(mail).to receive(:deliver_later)
  end
end
```

And don't forget to add it to your `rails_helper` to make it available.

**spec/rails_helper.rb**
```diff
require 'rails_helper'
+ require 'action_mailer_helper'
```

## Simplifying our test

With our new helper added we can really clean things up in our tests:

**spec/banana_spec.rb**
```ruby
it 'sends an :banana_split email after being #split' do
  expect(BananaMailer).to deliver_later(:banana_split)

  banana = Banana.new

  banana.split
end
```

Voila! 

A clear spec to make sure the Frosty Treat CEO will be happy for a long time.

