Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 611
Philoktetes Avatar asked Dec 09 '22 06:12

Philoktetes


1 Answers

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
like image 158
Dylan Markow Avatar answered Dec 23 '22 10:12

Dylan Markow