ActiveJob using delayed_job does not create delayed_jobs row in test

Viewed 693

Under Rails 4.2.3, I have setup ActiveJob to use delayed_job for its backend in all environments:

environment.rb

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module MyApp
  class Application < Rails::Application
    # ... snip
    # Use delayed_job for Active Job queueing
    config.active_job.queue_adapter = :delayed_job
  end
end

(and queue_adapter is not otherwise set under config).

I have configured my DelayedJob instance as follows - including code from this question to back-port the Rails 5 feature of getting the database row for a job:

config/delayed_job.rb

Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.sleep_delay = 60
Delayed::Worker.max_attempts = 3
Delayed::Worker.max_run_time = 5.minutes
Delayed::Worker.read_ahead = 10
# Delayed::Worker.delay_jobs = true #!Rails.env.test?
Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log'))

class Delayed::Job < ActiveRecord::Base
  belongs_to :owner, polymorphic: true
end

# Backport Rails 5 code to make the Delayed::Job database id avilable on ActiveJobs
module ActiveJob
  module Core
    # ID optionally provided by adapter
    attr_accessor :provider_job_id
  end

  module QueueAdapters
    class DelayedJobAdapter
      class << self
        send(:prepend, Module.new do
          def enqueue(job)
            provider_job = super
            job.provider_job_id = provider_job.id
            provider_job
          end

          def enqueue_at(job, timestamp)
            provider_job = super
            job.provider_job_id = provider_job.id
            provider_job
          end
        end)
      end
    end
  end
end

I have model code to perform a job later, and hook the Delayed::Job instance onto the model instance:

app/models/my_model.rb

def schedule
  job = MyJob.set(wait_until: scheduled_time).perform_later(self)

  # Associate the delayed_job object with the item that has been scheduled
  delayed_job = Delayed::Job.find(job.provider_job_id)
  delayed_job.owner = self
  delayed_job.save
end

(and I have set up appropriate associations for Delayed::Job).

This appears to work fine in development; but in test, the Delayed::Job.find(job.provider_job_id) fails and raises an exception, because provider_job_id is nil. Examining the logs shows no SQL INSERT for the delayed_jobs table.

Why is there no delayed job row being created in test?

1 Answers

I was having this same problem. Turns out that ActiveJob::TestCase hijacks the perform_later logic and doesn't actually queue them, but instead stores data about queued jobs in an array (see here).

My solution was to write a test that inherited from ActiveSupport::TestCase instead, which seems to queue delayed jobs like normal. I believe there are some test settings that will force jobs to be performed inline, but as far as I know, the default behavior is to queue them, so it should work as expected without much tinkering.

Related