I'm maintaining a suite of feature specs with lots of sidekiq jobs being run within tests. Everything was quite fine until there's been a need to enable javascript for all feature specs, and things suddenly got ugly. I'm not sure I understand reasons for that.
A typical problem looks like this: At the beginning of spec I invoke helper, that eventually starts Sidekiq job:
it 'does something fancy' do
Sidekiq::Testing.inline! do
page.attach_file('black_list_file', file)
click_on 'Import file'
end
expect(page).to have_content('Import started') # No problem here
expect(page).to have_link('imported_file.csv') # This fails
end
I tried putting sleep n
between expect
statements - no luck even with n = 10
. Then I tried to debug, and noticed something strange: ImportWorker.jobs
returns the job still in queue, and if I explicitly state ImportWorker.drain
from debugger, spec will pass.
But! If I put ImportWorker.drain
between expect
lines - it still fails, even if I put sleep
before drain, to wait for request to actually invoke perform_async
on worker.
And things get even more strange when I switch to selenium driver - everything goes smoothly, test passes.
Now, I have some insight into how requests made from capybara are processed in different thread from specs, as explained here. So probably, when worker is invoked, it has no idea about inline!
, and just puts the job into Redis queue. I still don't understand though why Selenium is capable of managing this issue. And if this is true, how do I test features, that involve sidekiq jobs, using webkit driver.
UPD.
Okay, as I expected Sidekiq::Testing.inline?
returns false at the place, where worker is called, even if request was made from inline!
block.
This solves all problems:
if Rails.env.test?
Sidekiq::Testing.inline! do
ImportWorker.perform_async(args)
end
else
ImportWorker.perform_async(args)
end
Obviously, this is as far away from best practices as possible, so I'm thinking of writing a class, that will accept worker name as a string or symbol, and call perform on it with or without inline!
block depending on env. This way I can keep this ugliness in one place, without the need to pollute controllers and models.
Please comment if you think this is a bad solution.