Skip to content

RSpec custom matcher for ActionMailer's deliver_later

License

Notifications You must be signed in to change notification settings

whylom/delivery_matchers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DeliveryMatchers

Build Status

An RSpec custom matcher for ActionMailer's deliver_later method.

This matcher was extracted from a General Assembly product with a considerable amount of logic to determine when to send certain transactional emails to different subsets of students.

We wanted to ensure this code had bulletproof test coverage. Inspecting the in-memory ActiveJob queue let us test our delivery logic, but it also allowed a lot of low-level details to leak into our tests, making them harder to read.

We rolled this custom matcher to keep our test code beautiful. We hope you find it as useful as we did!

Installation

Add this line to your application's Gemfile:

gem 'delivery_matchers'

and include the DeliveryMatchers module in your RSpec config:

RSpec.configure do |config|
  config.include DeliveryMatchers
end

Usage

Basics

Let's assume you have a garden-variety ActionMailer class:

class UserMailer < ActionMailer::Base
  def welcome(user)
    @user = user
  end
end

Let's further assume you have a controller action that enqueues a welcome email for delivery with deliver_later, but only under certain circumstances:

class UsersController < ApplicationController
  def create
    ...
    if user.emailable?
      UserMailer(user).welcome.deliver_later
    end
    ...
  end
end

With this custom matcher, you can write tests to assert that the controller enqueues an email for delivery.

expect( UserMailer.welcome(emailable_user) ).to be_delivered

You can also assert that an email is not delivered, in cases where that is the expected behavior.

expect( UserMailer.welcome(non_emailable_user) ).not_to be_delivered

Delivery options

wait_until

If you schedule an email for delivery on a future date with wait_until

UserMailer.welcome(user).deliver_later(wait_until: 1.day.from_now)

you can test it with

let(:email) { UserMailer.welcome(user) }
expect(email).to be_delivered 1.day.from_now

or with the more explicit (and sometimes more readable) form

expect(email).to be_delivered on: expected_date

wait

If you use wait to schedule an email for delivery after a certain interval

UserMailer.welcome(user).deliver_later(wait: 2.days)

you can test it like this

expect(email).to be_delivered in: 2.days

queue

You can also test that a delivery job was put into a particular queue

UserMailer.welcome(user).deliver_later(queue: "priority")

like this

expect(email).to be_delivered via_queue: "priority"

via_queue can also be combined with either the on or in options

expect(email).to be_delivered 3.days.from_now, via_queue: "priority"

expect(email).to be_delivered in: 2.days, via_queue: "priority"

Alternative interface

be_delivered supports in, on, and via_queue as a convenience for readability. But if you prefer consistency with the code being tested, you can also use the same keys provided to deliver_later.

expect(email).to be_delivered wait_until: expected_date

expect(email).to be_delivered wait: 2.days

expect(email).to be_delivered queue: "priority"

A note on precision

This matcher performs all time comparisons with a precision of 1 second. If you see intermittent errors as a result, consider using ActiveSupport's TimeHelpers or the Timecop gem to freeze time in your tests.

About

RSpec custom matcher for ActionMailer's deliver_later

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages