Philoktetes
Philoktetes

Reputation: 230

RSpec: Testing that a mailer is called exactly once

I have a Mailer that looks something like this:

class EventMailer < BaseMailer
  def event_added(user_id, event_id)
    # do stuff and email user about the event
  end
end

I'm calling this EventMailer like this from inside the Event class:

class Event < Task
  def notify_by_email(user)
    EmailLog.send_once(user.id, id) do
      EventMailer.delay(queue: 'mailer').event_added(user.id, id)
    end
  end
end

where EmailLog is a class that logs sent emails. .delay is added by Sidekiq.

But when I try to test that #notify_by_email is called only once per event and user, my spec fails:

1) Event#notify_by_email only sends once per user
     Failure/Error: expect(EventMailer).to receive(:event_added).once
       (<EventMailer (class)>).event_added(any args)
           expected: 1 time with any arguments
           received: 0 times with any arguments

The spec looks like:

let(:owner) { User.make! }
let(:product) { Product.make! }
let(:event) { Task.make!(user: owner, product: product) }

describe '#notify_by_email' do
  before do
    EventMailer.stub(:delay).and_return(EventMailer)
  end

  it 'only sends once per user' do
    event.notify_by_email(owner)
    event.notify_by_email(owner)

    expect(EventMailer).to receive(:event_added).once
  end
end

Any insights into why this spec is failing and how I can fix it? Strangely, if I put a puts statement inside the block that's passed to EmailLog.send_once, it prints only once, the spec still reports that EventMailer.event_added wasn't called.

Upvotes: 6

Views: 7318

Answers (1)

Dylan Markow
Dylan Markow

Reputation: 124469

Your expectation should be declared before the code you're testing. Using expect(...).to receive(...) basically means "this message should be received between now and the end of this spec". Because the expectation is the last line of your spec, it fails.

Try moving it before and you should be good to go:

it 'only sends once per user' do
  expect(EventMailer).to receive(:event_added).once

  event.notify_by_email(owner)
  event.notify_by_email(owner)
end

Upvotes: 16

Related Questions