I'm in the process of building an email reminder feature for students. They should receive an email at an interval that their instructor specifies indicating how much progress they have made.
My approach is to use ActiveJob to schedule a job that sends an email, then when the job is performed, I use the after_perform
callback to schedule the next job (taken from this post). Here's what that looks like:
class StudentReminderJob < ApplicationJob
queue_as :default
after_perform do |job|
# (get user update frequency preference)
self.class.set(wait: update_frequency).perform_later(job.arguments[0])
end
def perform(user)
# Send email containing student stats information
UserMailer.send_student_reminder_email(user)
end
end
The initial set of reminders will be kicked off by a rake task.
My issue is this: when trying to test the above code with perform_enqueued_jobs
I seem to be hitting an infinite loop. Here's my test:
require 'rails_helper'
describe StudentReminderJob, active_job: true, type: :job do
include ActiveJob::TestHelper
include ActiveSupport::Testing::TimeHelpers
let(:user) { create :user }
subject(:job) { described_class.perform_later(user) }
it 'gets enqueued' do
# Passes
expect { job }.to have_enqueued_job(described_class)
end
it 'receives perform later with user and base URL' do
# Passes (may be duplicatative)
expect(StudentReminderJob).to receive(:perform_later).with(user)
job
end
it 'calls UserMailer with #send_student_reminder_email when performed' do
# Fails - (SystemStackError: stack level too deep)
expect(UserMailer).to receive(:send_student_reminder_email)
perform_enqueued_jobs { job }
end
after do
clear_enqueued_jobs
clear_performed_jobs
end
end
The third it block fails with SystemStackError: stack level too deep
. Is there a way to only perform the one job in the queue and avoid performing the job from the callback?
Ideally, I'd then like to expand my test coverage to ensure that a new job is enqueued at a given date and time.