Skip to content

Commit

Permalink
ActiveJob performed matchers
Browse files Browse the repository at this point in the history
* `have_been_performed` is like `have_been_enqueued`, but for performed
  jobs
* `have_performed_job` is like `have_enqueued_job`, but for performed
  jobs
  • Loading branch information
isaacseymour committed Jan 31, 2017
1 parent 82340ff commit 627d979
Show file tree
Hide file tree
Showing 5 changed files with 548 additions and 20 deletions.
8 changes: 5 additions & 3 deletions features/job_specs/job_spec.feature
Expand Up @@ -15,9 +15,11 @@ Feature: job spec
* specify the queue which the job was enqueued to

Check the documentation on
[`have_been_enqueued`](matchers/have-been-enqueued-matcher) and
[`have_enqueued_job`](matchers/have-enqueued-job-matcher) for more
information.
[`have_been_enqueued`](matchers/have-been-enqueued-matcher),
[`have_enqueued_job`](matchers/have-enqueued-job-matcher),
[`have_been_performed`](matchers/have-been-performed-matcher), and
[`have_performed_job`](matchers/have-performed-job-matcher)
for more information.

Background:
Given active job is available
Expand Down
76 changes: 76 additions & 0 deletions features/matchers/have_been_performed_matcher.feature
@@ -0,0 +1,76 @@
Feature: have_been_performed matcher

The `have_been_performed` matcher is used to check if given ActiveJob job was performed.

Background:
Given active job is available

Scenario: Checking job class name
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adapter.perform_performed_jobs = true
UploadBackupsJob.perform_later
expect(UploadBackupsJob).to have_been_performed
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking passed arguments to job
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adapter.perform_performed_jobs = true
UploadBackupsJob.perform_later("users-backup.txt", "products-backup.txt")
expect(UploadBackupsJob).to(
have_been_performed.with("users-backup.txt", "products-backup.txt")
)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking job performed time
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adapter.perform_performed_jobs = true
UploadBackupsJob.set(:wait_until => Date.tomorrow.noon).perform_later
expect(UploadBackupsJob).to have_been_performed.at(Date.tomorrow.noon)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking job queue name
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adapter.perform_performed_jobs = true
UploadBackupsJob.perform_later
expect(UploadBackupsJob).to have_been_performed.on_queue("default")
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass
96 changes: 96 additions & 0 deletions features/matchers/have_performed_job_matcher.feature
@@ -0,0 +1,96 @@
Feature: have_performed_job matcher

The `have_performed_job` (also aliased as `enqueue_job`) matcher is used to check if given ActiveJob job was performed.

Background:
Given active job is available

Scenario: Checking job class name
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adatper.perform_enqueued_jobs = true
expect {
UploadBackupsJob.perform_later
}.to have_performed_job(UploadBackupsJob)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking passed arguments to job
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adatper.perform_enqueued_jobs = true
expect {
UploadBackupsJob.perform_later("users-backup.txt", "products-backup.txt")
}.to have_performed_job.with("users-backup.txt", "products-backup.txt")
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking job performed time
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adatper.perform_enqueued_jobs = true
expect {
UploadBackupsJob.set(:wait_until => Date.tomorrow.noon).perform_later
}.to have_performed_job.at(Date.tomorrow.noon)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Checking job queue name
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adatper.perform_enqueued_jobs = true
expect {
UploadBackupsJob.perform_later
}.to have_performed_job.on_queue("default")
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass

Scenario: Using alias method
Given a file named "spec/jobs/upload_backups_job_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe UploadBackupsJob do
it "matches with performed job" do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adatper.perform_enqueued_jobs = true
expect {
UploadBackupsJob.perform_later
}.to enqueue_job(UploadBackupsJob)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass
115 changes: 110 additions & 5 deletions lib/rspec/rails/matchers/active_job.rb
Expand Up @@ -11,7 +11,9 @@ module ActiveJob
# rubocop: disable Style/ClassLength
# @private
class Base < RSpec::Matchers::BuiltIn::BaseMatcher
def initialize
def initialize(verb_present_tense, verb_past_tense)
@verb_present_tense = verb_present_tense
@verb_past_tense = verb_past_tense
@args = []
@queue = nil
@at = nil
Expand Down Expand Up @@ -67,7 +69,7 @@ def thrice
end

def failure_message
"expected to enqueue #{base_message}".tap do |msg|
"expected to #{@verb_present_tense} #{base_message}".tap do |msg|
if @unmatching_jobs.any?
msg << "\nQueued jobs:"
@unmatching_jobs.each do |job|
Expand All @@ -78,7 +80,7 @@ def failure_message
end

def failure_message_when_negated
"expected not to enqueue #{base_message}"
"expected not to #{@verb_present_tense} #{base_message}"
end

def message_expectation_modifier
Expand Down Expand Up @@ -119,7 +121,7 @@ def base_message
msg << " with #{@args}," if @args.any?
msg << " on queue #{@queue}," if @queue
msg << " at #{@at}," if @at
msg << " but enqueued #{@matching_jobs_count}"
msg << " but #{@verb_past_tense} #{@matching_jobs_count}"
end
end

Expand Down Expand Up @@ -174,7 +176,7 @@ def queue_adapter
# @private
class HaveEnqueuedJob < Base
def initialize(job)
super()
super("enqueue", "enqueued")
@job = job
end

Expand All @@ -191,11 +193,45 @@ def matches?(proc)

# @private
class HaveBeenEnqueued < Base
def initialize
super("enqueue", "enqueued")
end

def matches?(job)
@job = job
check(queue_adapter.enqueued_jobs)
end
end

# @private
class HavePerformedJob < Base
def initialize(job)
super("perform", "performed")
@job = job
end

def matches?(proc)
raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc

original_performed_jobs_count = queue_adapter.performed_jobs.count
proc.call
in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)

check(in_block_jobs)
end
end

# @private
class HaveBeenPerformed < Base
def initialize
super("perform", "performed")
end

def matches?(job)
@job = job
check(queue_adapter.performed_jobs)
end
end
end

# @api public
Expand Down Expand Up @@ -269,6 +305,75 @@ def have_been_enqueued
ActiveJob::HaveBeenEnqueued.new
end

# @api public
# Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
#
# @example
# expect {
# perform_jobs { HeavyLiftingJob.perform_later }
# }.to have_performed_job
#
# expect {
# perform_jobs {
# HelloJob.perform_later
# HeavyLiftingJob.perform_later
# }
# }.to have_performed_job(HelloJob).exactly(:once)
#
# expect {
# perform_jobs { 3.times { HelloJob.perform_later } }
# }.to have_performed_job(HelloJob).at_least(2).times
#
# expect {
# perform_jobs { HelloJob.perform_later }
# }.to have_performed_job(HelloJob).at_most(:twice)
#
# expect {
# perform_jobs {
# HelloJob.perform_later
# HeavyLiftingJob.perform_later
# }
# }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
#
# expect {
# perform_jobs {
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
# }
# }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
def have_performed_job(job = nil)
check_active_job_adapter
ActiveJob::HavePerformedJob.new(job)
end

# @api public
# Passes if a job has been preformed. May chain at_least, at_most or exactly to specify a number of times.
#
# @example
# before { ActiveJob::Base.queue_adapter.performed_jobs.clear }
# before { ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true }
#
# HeavyLiftingJob.perform_later
# expect(HeavyLiftingJob).to have_been_performed
#
# HelloJob.perform_later
# HeavyLiftingJob.perform_later
# expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
#
# 3.times { HelloJob.perform_later }
# expect(HelloJob).to have_been_performed.at_least(2).times
#
# HelloJob.perform_later
# HeavyLiftingJob.perform_later
# expect(HelloJob).to have_been_performed
# expect(HeavyLiftingJob).to have_been_performed
#
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
# expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
def have_been_performed
check_active_job_adapter
ActiveJob::HaveBeenPerformed.new
end

private

# @private
Expand Down

0 comments on commit 627d979

Please sign in to comment.