Skip to content

Commit 3fa4d25

Browse files
committed
Merge pull request #1480 from morgoth/improve-aj-matcher
Add more methods to have_enqueued_job matcher
2 parents c3ac231 + ebe3867 commit 3fa4d25

File tree

2 files changed

+113
-13
lines changed

2 files changed

+113
-13
lines changed

lib/rspec/rails/matchers/active_job.rb

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "active_job/base"
2+
require "active_job/arguments"
23

34
module RSpec
45
module Rails
@@ -11,23 +12,45 @@ module ActiveJob
1112
class HaveEnqueuedJob < RSpec::Matchers::BuiltIn::BaseMatcher
1213
def initialize(job)
1314
@job = job
15+
@args = []
16+
@queue = nil
17+
@at = nil
1418
set_expected_number(:exactly, 1)
1519
end
1620

1721
def matches?(proc)
1822
raise ArgumentError, "have_enqueued_jobs only supports block expectations" unless Proc === proc
1923

20-
before_block_jobs_size = enqueued_jobs_size(@job)
24+
original_enqueued_jobs_count = queue_adapter.enqueued_jobs.count
2125
proc.call
22-
@in_block_jobs_size = enqueued_jobs_size(@job) - before_block_jobs_size
26+
in_block_jobs = queue_adapter.enqueued_jobs.drop(original_enqueued_jobs_count)
27+
28+
@matching_jobs_count = in_block_jobs.count do |job|
29+
serialized_attributes.all? { |key, value| value == job[key] }
30+
end
2331

2432
case @expectation_type
25-
when :exactly then @expected_number == @in_block_jobs_size
26-
when :at_most then @expected_number >= @in_block_jobs_size
27-
when :at_least then @expected_number <= @in_block_jobs_size
33+
when :exactly then @expected_number == @matching_jobs_count
34+
when :at_most then @expected_number >= @matching_jobs_count
35+
when :at_least then @expected_number <= @matching_jobs_count
2836
end
2937
end
3038

39+
def with(*args)
40+
@args = args
41+
self
42+
end
43+
44+
def on_queue(queue)
45+
@queue = queue
46+
self
47+
end
48+
49+
def at(date)
50+
@at = date
51+
self
52+
end
53+
3154
def exactly(count)
3255
set_expected_number(:exactly, count)
3356
self
@@ -48,11 +71,11 @@ def times
4871
end
4972

5073
def failure_message
51-
"expected to enqueue #{message_expectation_modifier} #{@expected_number} jobs, but enqueued #{@in_block_jobs_size}"
74+
"expected to enqueue #{base_message}"
5275
end
5376

5477
def failure_message_when_negated
55-
"expected not to enqueue #{message_expectation_modifier} #{@expected_number} jobs, but enqueued #{@in_block_jobs_size}"
78+
"expected not to enqueue #{base_message}"
5679
end
5780

5881
def message_expectation_modifier
@@ -69,11 +92,21 @@ def supports_block_expectations?
6992

7093
private
7194

72-
def enqueued_jobs_size(job)
73-
if job
74-
queue_adapter.enqueued_jobs.count { |enqueued_job| job == enqueued_job.fetch(:job) }
75-
else
76-
queue_adapter.enqueued_jobs.count
95+
def base_message
96+
"#{message_expectation_modifier} #{@expected_number} jobs,".tap do |msg|
97+
msg << " with #{@args}," if @args.any?
98+
msg << " on queue #{@queue}," if @queue
99+
msg << " at #{@at}," if @at
100+
msg << " but enqueued #{@matching_jobs_count}"
101+
end
102+
end
103+
104+
def serialized_attributes
105+
{}.tap do |attributes|
106+
attributes[:args] = ::ActiveJob::Arguments.serialize(@args) if @args.any?
107+
attributes[:at] = @at.to_f if @at
108+
attributes[:queue] = @queue if @queue
109+
attributes[:job] = @job if @job
77110
end
78111
end
79112

@@ -120,6 +153,10 @@ def queue_adapter
120153
# HelloJob.perform_later
121154
# HeavyLiftingJob.perform_later
122155
# }.to have_enqueued_job(HelloJob).and have_enqueued_job(HeavyLiftingJob)
156+
#
157+
# expect {
158+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
159+
# }.to have_enqueued_job.with(42).on_queue("low").at(Date.tomorrow.noon)
123160
def have_enqueued_job(job = nil)
124161
ActiveJob::HaveEnqueuedJob.new(job)
125162
end

spec/rspec/rails/matchers/active_job_spec.rb

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ def perform; end
1515

1616
let(:hello_job) do
1717
Class.new(ActiveJob::Base) do
18-
def perform; end
18+
def perform(*)
19+
end
1920
end
2021
end
2122

@@ -25,6 +26,30 @@ def perform; end
2526
end
2627
end
2728

29+
let(:global_id_model) do
30+
Class.new do
31+
include GlobalID::Identification
32+
33+
attr_reader :id
34+
35+
def self.find(id)
36+
new(id)
37+
end
38+
39+
def self.name
40+
"AnonymousClass"
41+
end
42+
43+
def initialize(id)
44+
@id = id
45+
end
46+
47+
def to_global_id(options = {})
48+
@global_id ||= GlobalID.create(self, :app => "rspec-suite")
49+
end
50+
end
51+
end
52+
2853
before do
2954
ActiveJob::Base.queue_adapter = :test
3055
end
@@ -144,5 +169,43 @@ def perform; end
144169
}.to have_enqueued_job.at_most(:once)
145170
}.to raise_error(/expected to enqueue at most 1 jobs, but enqueued 2/)
146171
end
172+
173+
it "passes with provided queue name" do
174+
expect {
175+
hello_job.set(:queue => "low").perform_later
176+
}.to have_enqueued_job.on_queue("low")
177+
end
178+
179+
it "passes with provided at date" do
180+
date = Date.tomorrow.noon
181+
expect {
182+
hello_job.set(:wait_until => date).perform_later
183+
}.to have_enqueued_job.at(date)
184+
end
185+
186+
it "passes with provided arguments" do
187+
expect {
188+
hello_job.perform_later(42, "David")
189+
}.to have_enqueued_job.with(42, "David")
190+
end
191+
192+
it "passes with provided arguments containing global id object" do
193+
global_id_object = global_id_model.new(42)
194+
195+
expect {
196+
hello_job.perform_later(global_id_object)
197+
}.to have_enqueued_job.with(global_id_object)
198+
end
199+
200+
it "generates failure message with all provided options" do
201+
date = Date.tomorrow.noon
202+
message = "expected to enqueue exactly 2 jobs, with [42], on queue low, at #{date}, but enqueued 0"
203+
204+
expect {
205+
expect {
206+
hello_job.perform_later
207+
}.to have_enqueued_job(hello_job).with(42).on_queue("low").at(date).exactly(2).times
208+
}.to raise_error(message)
209+
end
147210
end
148211
end

0 commit comments

Comments
 (0)