Search code examples
rspecruby-on-rails-5rake-task

rake task rspec test does not respond to :have_received method


I have a pretty simple rake task:

namespace :subscriptions do
  desc 'Send expired subscription notifications'
  task notify_trial_expired: :environment do
    Subscription.where(expires_at: Date.today.all_day).each { |s| s.user.notify_trial_expired }
  end
end

Where notify_trial_expired is an instance method of the model User. The task works fine tested manually.

Now using rspec this is what I wrote:

require 'rails_helper'

describe "notify_trial_expired" do
  let!(:subscription) { create(:subscription, expires_at: Date.today) }
  let(:user) { double(:user) }

  before do
    allow(subscription).to receive(:user) { user }
    allow(user).to receive(:notify_trial_expired)
    Rake.application.rake_require "tasks/subscriptions"
    Rake::Task.define_task(:environment)
    Rake::Task['subscriptions:notify_trial_expired'].invoke
  end

  it "should notify all user which trial expires today" do
    expect(user).to have_received(:notify_trial_expired)
  end
end

I also tried with expect(user).to receive and invoking the task after but both ways it shows the same error:

Failure/Error: expect(user).to have_received(:notify_trial_expired)

       (Double :user).notify_trial_expired(*(any args))
           expected: 1 time with any arguments
           received: 0 times with any arguments

I also checked to be sure the query Subscription.where(expires_at: Date.today.all_day) returns my subscription and it does. It's in received or have_received method the issue.


Solution

  • The issue is

    allow(subscription).to receive(:user) { user }
    

    The object subscription here is not the same object returned from your query: Subscription.where(expires_at: Date.today.all_day). Yes in all practical terms they are the same record, but not from the tests point of view (the object_id's are different).

    Your query looks dead simple, so I'd stub it (or move it to a model scope and test it there).

    allow(Subscription).to receive(:where).with(expires_at: Date.today).and_return([subscription])
    

    Now the rest of your stubs should work.