I have some complex, long-running delayed_job processes in my application. I am using Rspec to test individual methods and classes used in the processes, but I would also like to perform many of end-to-end background jobs, with different test data.
I couldn't find anything on the delayed_job wiki on this, and this SO question looks interesting but I didn't really understand what is happening here. What's the best way to test delayed_job chains with rSpec?
I can easily set up the test data with a factory, and then call the class that starts the background processing. I expect the tests to take a long time to complete.
edited background code
class Singleplex
def perform(batch_id,user)
batch = start_batch(batch_id,user)
... do lots of stuff ...
end
handle_asynchronously :perform, queue: :singleplex, :run_at => Proc.new { 1.second.from_now }
spec/factories/batches.rb
FactoryGirl.define do
factory :batch do
batch_type 'singleplex'
name 'valid panel'
status 'ready'
end
factory :batch_detail do
chrom 7
chrom_start 140435012
chrom_end 140435012
target_offset 150
padding 4
primer3_parameter_id 1
snp_mask 't'
status 'ready'
batch
end
end
Then run the test like this
describe Batch do
it 'runs Singleplex for a valid panel' do
batch = FactoryGirl.create(:batch)
user = User.find(1)
status = Singleplex.new.perform(batch.id,user)
expect(status.should == true)
end
end
I have two problems to solve:
1) How to tell the test to wait until the delayed_job call is completed before validating the results ?
2) To validate the results I will need to check values in multiple tables. What is the best way to do this in Rspec?
EDIT
I should add I get a delayed_job object, so of course the status check fails. The jobs take at least 10 minutes usually.
1) Batch runs Singleplex for a valid panel
Failure/Error: expect(status.should == true)
expected: true
got: #<Delayed::Backend::ActiveRecord::Job id: nil, priority: 0, attempts: 0, handler: "--- !ruby/object:Delayed::PerformableMethod\nobject:...", last_error: nil, run_at: nil, locked_at: nil, failed_at: nil, locked_by: nil, queue: nil, created_at: nil, updated_at: nil> (using ==)
There are a few ways to go about doing this. All of them require that you execute the job in your code.
Method 1: A test which queues the job and then tells the DelayedJob::Worker
to complete it.
describe Batch do
it 'runs Singleplex for a valid panel' do
batch = FactoryGirl.create(:batch)
user = User.find(1)
Singleplex.new.perform(batch.id,user)
expect(Delayed::Worker.new.work_off).to eq [1, 0] # Returns [successes, failures]
# Add expectations which check multiple tables to make sure the work is done
end
end
Method 2: A test which runs the job in question with queueing disabled, and checks for the desired results. You can delay queuing by calling Delayed::Worker.delay_jobs = false
somewhere in your testing configuration or in a before
block.
before(:each) do
Delayed::Worker.delay_jobs = false
end
describe Batch do
it 'runs Singleplex for a valid panel' do
batch = FactoryGirl.create(:batch)
user = User.find(1)
Singleplex.new.perform(batch.id,user)
# expectations which check that the work is done
end
end
This method has, however, been known to cause issues with callbacks.
Method 3: Write an observer that watches for any new jobs that are created and runs them. This way you won't have to manually declare "work_off" in your tests. Artsy has a gist for this.
It's also a good idea to have tests elsewhere that make sure jobs get queued as expected
it "queues welcome when a user is created" do
expect(Delayed::Job.count).to eq 0
# Create user step
expect(Delayed::Job.count).to eq 1 # You should really be looking for the count of a specific job.
end