Rails provides perform_enqueued_jobs
test helper to perform all enqueued jobs in the given block while testing.
perform_enqueued_jobs do
TestJob.perform_later("Hello") # will be performed
end
assert_performed_jobs 1
Let’s consider we have a Shift
model
which has start_time
and end_time
as timestamps
and status
to maintain shift’s status.
Shift status can be started
or finished
.
class Shift
enum status: [:started, :finished]
after_create :auto_finish_shift
def auto_finish_shift
ShiftAutoFinishJob.set(wait_until: end_time).perform_later(id)
end
end
We have after_save
callback to schedule ShiftAutoFinishJob
which will wait until shift’s end_time
.
ShiftAutoFinishJob
is used here to automatically finish the shift
.
class ShiftAutoFinishJob < ApplicationJob
queue_as :default
def perform(shift_id)
shift = Shift.find(shift_id)
shift.update status: 'finished'
end
end
require 'test_helper'
class ShiftTest < ActiveSupport::TestCase
test "success" do
perform_enqueued_jobs do
create :shift, start_time: Time.now, end_time: Time.now + 100, status: 'started'
end
assert_performed_jobs 0
end
end
> rails test TEST=test/models/shift_test
# Running:
F
Failure:
ShiftTest#test_success [/test/models/shift_test.rb:9]:
0 jobs expected, but 1 were performed.
Expected: 0
Actual: 1
We can see that ShiftAutoFinishJob
has been performed. It should not be executed because shift’s end_time
is later than the Time.now
.
To avoid this, Rails 6.1 has added
at
option to perform_enqueued_jobs
test helper to run only jobs which are scheduled at or before the passed in time.
With Rails 6.1
require 'test_helper'
class ShiftTest < ActiveSupport::TestCase
test "success" do
perform_enqueued_jobs(at: Time.now) do
create :shift, start_time: Time.now, end_time: Time.now + 100, status: 'started'
end
assert_performed_jobs 0
end
end
> rails test TEST=test/models/shift_test
# Running:
.
Finished in 0.579221s, 1.7265 runs/s, 1.7265 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
In the example above, ShiftAutoFinishJob
has not been executed because the shift’s end_time
is later than the passed in time, i.e. Time.now
.