Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveJob performed matchers #1785

Merged
merged 1 commit into from Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.md
Expand Up @@ -16,6 +16,8 @@ Enhancements:
(David Revelo, #2134)
* Add argument matcher support to `have_enqueued_*` matchers. (Phil Pirozhkov, #2206)
* Switch generated templates to use ruby 1.9 hash keys. (Tanbir Hasan, #2224)
* Add `have_been_performed`/`have_performed_job`/`perform_job` ActiveJob
matchers (Isaac Seymour, #1785)

Bug Fixes:

Expand Down
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
77 changes: 77 additions & 0 deletions features/matchers/have_been_performed_matcher.feature
@@ -0,0 +1,77 @@
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_enqueued_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_enqueued_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_enqueued_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_at_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_enqueued_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
97 changes: 97 additions & 0 deletions features/matchers/have_performed_job_matcher.feature
@@ -0,0 +1,97 @@
Feature: have_performed_job matcher

The `have_performed_job` (also aliased as `perform_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_adapter.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_adapter.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_adapter.perform_enqueued_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_at_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_adapter.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_adapter.perform_enqueued_jobs = true
expect {
UploadBackupsJob.perform_later
}.to perform_job(UploadBackupsJob)
end
end
"""
When I run `rspec spec/jobs/upload_backups_job_spec.rb`
Then the examples should all pass
117 changes: 114 additions & 3 deletions lib/rspec/rails/matchers/active_job.rb
Expand Up @@ -67,7 +67,7 @@ def thrice
end

def failure_message
"expected to enqueue #{base_message}".tap do |msg|
"expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
JonRowe marked this conversation as resolved.
Show resolved Hide resolved
if @unmatching_jobs.any?
msg << "\nQueued jobs:"
@unmatching_jobs.each do |job|
Expand All @@ -78,7 +78,7 @@ def failure_message
end

def failure_message_when_negated
"expected not to enqueue #{base_message}"
"expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
end

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

Expand Down Expand Up @@ -193,6 +193,9 @@ def queue_adapter

# @private
class HaveEnqueuedJob < Base
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze

def initialize(job)
super()
@job = job
Expand All @@ -217,6 +220,9 @@ def does_not_match?(proc)

# @private
class HaveBeenEnqueued < Base
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze

def matches?(job)
@job = job
check(queue_adapter.enqueued_jobs)
Expand All @@ -228,6 +234,38 @@ def does_not_match?(proc)
!matches?(proc)
end
end

# @private
class HavePerformedJob < Base
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze

def initialize(job)
super()
@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)
JonRowe marked this conversation as resolved.
Show resolved Hide resolved

check(in_block_jobs)
end
end

# @private
class HaveBeenPerformed < Base
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze

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

# @api public
Expand Down Expand Up @@ -315,6 +353,79 @@ 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
alias_method :perform_job, :have_performed_job

# @api public
# Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
#
# @example
# before do
# ActiveJob::Base.queue_adapter.performed_jobs.clear
# ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
# ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
# end
#
# 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