Hello everyone !
I've got to report a strange behavior that I observed today with the use of have_enqueud_job
Ruby version: ruby 2.4.1
Rails version: 4.2.10
Rspec version: 3.7
Consider the following code :
class MyJob < ActiveJob::Base
def perform(param)
puts "Hello world ! #{param}"
end
end
class JobLauncher
def launch
MyJob.perform_later("first job")
MyJob.perform_later("second job")
end
end
RSpec.describe JobLauncher do
describe "#launch" do
it "works" do
expect {
JobLauncher.new.launch
}.to have_enqueued_job(MyJob)
end
end
end
This spec is failing because two jobs have been enqueued with the following message :
expected to enqueue exactly 1 jobs, but enqueued 2
There is nothing wrong with that and that's the behavior expected.
But when you change the expectation line with :
expect {
JobLauncher.new.launch
}.to have_enqueued_job(MyJob).with("first job")
then the spec does not fail anymore, even though there is still two jobs that have been enqueued.
I find this difference of behavior very strange. I would have expected that have_enqueued_job(Job).with(params) would behave the same as without the with, just checking the job params extra.
Maybe it is not a bug but a feature. If so, is there a way to check that only one job have been enqueued with RSpec ?
Thank you
Hello
Thanks for your detailed report.
Just a quick look. It seems we are not validating class only job count. https://github.com/rspec/rspec-rails/blob/v3.8.0/lib/rspec/rails/matchers/active_job.rb#L188
I will try to have a look even if I don't have a lot of time at the moment. Feel free to open a PR if you can dig into this subject.
The issue is we don't have a registry of enqueued jobs used / expected unlike how we do with expectations of methods etc, so we don't have global knowledge of whats expected, so "unexpected" jobs are just allowed through, this might take some work to fix.
As a work around this should work for now:
expect {
JobLauncher.new.launch
}.to have_enqueued_job(MyJob) { |args| expect(args).to eq ['first job'] }
We could make the default expected number relativity :at_least- This breaks 3 specs, but only the expected error message. This changes the default behavior of the HaveEnqueuedJob matcher, but that can be restored by chaining the #exactly method on the matcher if someone is relying on the default exactly.
I've opened PR https://github.com/rspec/rspec-rails/pull/2033 to continue the conversation
Similiar maybe? https://github.com/rspec/rspec-rails/pull/1973
just to add my two cents. I found it very unexpected that this is using an exactly 1 match. it should be checking at least 1 not failing if there are more
@SampsonCrowley the behaviour you are looking for can be provided if you add at_least(:once), as has been discussed on #1973, the current behaviour is in line with all of our other matchers
@JonRowe I'm aware, thank you. I'm just adding my opinion for the next major release.
I'm saying the name of the rule doesn't match expected behavior, by English logic. If two of a job are enqueued, then the job has been enqueued. The default should be an at_least matcher, and should be able to chain an exactly matcher if desired. Or even better would be a config option to default these types of scenarios to the user's preference.
@SampsonCrowley The logic comes from the fact that duplicate calls are more likely to be an error than a deliberate decision, thus when its deliberate you can tell us, removing silent errors. Its not going to be changed.
Is there anything actionable left?