What's The Best Way to Test Delayed_Job Chains with Rspec

What's the best way to test delayed_job chains with rSpec?

Here are some discussions about chaining methods in rSpec that I found helpful:

Stubbing Chained Methods with Rspec

http://groups.google.com/group/rspec/browse_thread/thread/6b8394836d2390b0#

Rspec testing delayed_job

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

how to test delayed_job callback hooks using RSpec

IMHO i don't think this is the best thing to do...

By doing this, you're trying to spec DelayedJob's behavior, not your app, and DJ already has its own suit of tests.

Anyway, what about mocking DelayedJob itself? Maybe you can specify, by doing that, that your MockedJob will call your hook after processing, and you can create your matchers/expectations accordingly...

Test delayed job mailers in Rails method

It sounds like what you want is to ensure that the update_review method enqueues a job to send the correct email to the correct recipient. Here's a simpler way to accomplish that:

describe 'PUT #update the review' do
let(:params) { { rating: rating, raw_review: raw_review } }
let(:rating) { 3.0 }
let(:raw_review) { 'Some example review here' }
let(:review) { FactoryBot.create(:review) }
let(:delayed_review_mailer) { instance_double(ReviewMailer) }

before do
# assuming this is how the controller finds the review...
allow(Review).to receive(:find).and_return(review)

# mock the method chain that enqueues the job to send the email
allow(ReviewMailer).to receive(:delay).and_return(delayed_review_mailer)
allow(delayed_review_mailer).to receive(:review_posted)

put :update, id: review.id review: params
end

it 'adds the review content to the review' do
review.reload
expect(review.rating).to eq(rating)
expect(review.raw_review).to eq(raw_review)
end

it 'sends a delayed email' do
expect(ReviewMailer).to have_received(:delay)
end

it 'sends a review posted email to the product owner' do
expect(delayed_review_mailer)
.to have_received(:review_posted)
.with(review.product_owner, review.id)
end
end

The reason I prefer this approach is that a) it could be done without touching the database at all (by swapping the factory for an instance double), and b) it doesn't try to test parts of Rails that were already tested by the folks who built Rails, like ActiveJob and ActionMailer. You can trust Rails' own unit tests for those classes.

Stubbing Chained Methods with Rspec

I figured something out.

Client.stub!(:named_scope).and_return(@clients = mock([Client]))
@clients.stub!(:first).and_return(@client = mock(Client))

which allows me to call my controller:

@client = Client.named_scope(param).first

It works, but is there a better solution?

EDIT:

The release of rspec 1.2.6 allows us to use stub_chain meaning it can now be:

Client.stub_chain(:named_scope, :chained_call).and_return(@clients = [mock(Client)])

This was top of my head, as always check the api for specifics :)

in delayed_job, are hooks called when Delayed::Worker.delay_jobs is false?

On the collectiveidea/delayed_job github page, I found this very same bug described, fixed, and pulled. Presumably an updated version of delayed_job will fix the problem.

Update: I've found a workaround other than pulling the latest version. You can explicitly call the Delayed::Job worker method. It will process items in the queue -- in the same thread as the tests of course -- but the callback hooks do get called:

[successes, failures] = Delayed::Worker.new.work_off

How to test ActionMailer deliver_later with rspec

If I understand you correctly, you could do:

message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(ServiceMailer).to receive(:new_user).with(@user).and_return(message_delivery)
allow(message_delivery).to receive(:deliver_later)

The key thing is that you need to somehow provide a double for deliver_later.



Related Topics



Leave a reply



Submit