Skip to content

Commit

Permalink
Merge pull request #48585 from ghiculescu/active-job-test-adapter
Browse files Browse the repository at this point in the history
Active Job: Correctly use the desired test adapter in tests
  • Loading branch information
byroot committed May 9, 2024
2 parents 25f2250 + 2194a73 commit 80fa112
Show file tree
Hide file tree
Showing 12 changed files with 1,882 additions and 1,451 deletions.
13 changes: 13 additions & 0 deletions activejob/CHANGELOG.md
@@ -1,3 +1,16 @@
* All tests now respect the `active_job.queue_adapter` config

Previously if you had set `config.active_job.queue_adapter` in your `config/application.rb`
or `config/environments/test.rb` file, the adapter you selected was previously not used consistently
across all tests. In some tests your adapter would be used, but other tests would use the `TestAdapter`.

In Rails 7.2, all tests will respect the `queue_adapter` config if provided. If no config is provided,
the `TestAdapter` will continue to be used.

See [#48585](https://github.com/rails/rails/pull/48585) for more details.

*Alex Ghiculescu*

* Make Active Job transaction aware when used conjointly with Active Record.

A common mistake with Active Job is to enqueue jobs from inside a transaction,
Expand Down
9 changes: 5 additions & 4 deletions activejob/lib/active_job/queue_adapter.rb
Expand Up @@ -15,7 +15,8 @@ def adapter_name(adapter) # :nodoc:
# = Active Job Queue adapter
#
# The +ActiveJob::QueueAdapter+ module is used to load the
# correct adapter. The default queue adapter is the +:async+ queue.
# correct adapter. The default queue adapter is +:async+,
# which loads the ActiveJob::QueueAdapters::AsyncAdapter.
module QueueAdapter # :nodoc:
extend ActiveSupport::Concern

Expand All @@ -24,21 +25,21 @@ module QueueAdapter # :nodoc:
class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false

delegate :queue_adapter, to: :class

self.queue_adapter = :async
end

# Includes the setter method for changing the active queue adapter.
module ClassMethods
# Returns the backend queue provider. The default queue adapter
# is the +:async+ queue. See QueueAdapters for more information.
# is +:async+. See QueueAdapters for more information.
def queue_adapter
self.queue_adapter = :async if _queue_adapter.nil?
_queue_adapter
end

# Returns string denoting the name of the configured queue adapter.
# By default returns <tt>"async"</tt>.
def queue_adapter_name
self.queue_adapter = :async if _queue_adapter_name.nil?
_queue_adapter_name
end

Expand Down
2 changes: 1 addition & 1 deletion activejob/lib/active_job/railtie.rb
Expand Up @@ -41,7 +41,7 @@ class Railtie < Rails::Railtie # :nodoc:

initializer "active_job.set_configs" do |app|
options = app.config.active_job
options.queue_adapter ||= :async
options.queue_adapter ||= (Rails.env.test? ? :test : :async)

config.after_initialize do
options.each do |k, v|
Expand Down
32 changes: 27 additions & 5 deletions activejob/lib/active_job/test_helper.rb
Expand Up @@ -39,10 +39,12 @@ def enable_test_adapter(test_adapter)
end

def before_setup # :nodoc:
test_adapter = queue_adapter_for_test

queue_adapter_changed_jobs.each do |klass|
klass.enable_test_adapter(test_adapter)
if (queue_adapter_specific_to_this_test_class = queue_adapter_for_test)
klass.enable_test_adapter(queue_adapter_specific_to_this_test_class)
elsif klass._queue_adapter.nil?
klass.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
end
end

clear_enqueued_jobs
Expand All @@ -61,7 +63,6 @@ def after_teardown # :nodoc:
# Override this method to specify a different adapter. The adapter must
# implement the same interface as ActiveJob::QueueAdapters::TestAdapter.
def queue_adapter_for_test
ActiveJob::QueueAdapters::TestAdapter.new
end

# Asserts that the number of enqueued jobs matches the given number.
Expand Down Expand Up @@ -118,6 +119,8 @@ def queue_adapter_for_test
# end
# end
def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
require_active_job_test_adapter!("assert_enqueued_jobs")

if block_given?
original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)

Expand Down Expand Up @@ -180,6 +183,8 @@ def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
#
# assert_enqueued_jobs 0, &block
def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
require_active_job_test_adapter!("assert_no_enqueued_jobs")

assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
end

Expand Down Expand Up @@ -270,6 +275,8 @@ def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
# end
# end
def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
require_active_job_test_adapter!("assert_performed_jobs")

if block_given?
original_count = performed_jobs.size

Expand Down Expand Up @@ -338,6 +345,8 @@ def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
#
# assert_performed_jobs 0, &block
def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
require_active_job_test_adapter!("assert_no_performed_jobs")

assert_performed_jobs 0, only: only, except: except, queue: queue, &block
end

Expand Down Expand Up @@ -394,6 +403,8 @@ def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
# end
# end
def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
require_active_job_test_adapter!("assert_enqueued_with")

expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
expected_args = prepare_args_for_assertion(expected)
potential_matches = []
Expand Down Expand Up @@ -496,6 +507,8 @@ def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, priority: nil
# end
# end
def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
require_active_job_test_adapter!("assert_performed_with")

expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
expected_args = prepare_args_for_assertion(expected)
potential_matches = []
Expand Down Expand Up @@ -604,7 +617,10 @@ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, priority: ni
# If queue_adapter_for_test is overridden to return a different adapter,
# +perform_enqueued_jobs+ will merely execute the block.
def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
unless block_given?
require_active_job_test_adapter!("perform_enqueued_jobs (without a block)")
return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at)
end

return _assert_nothing_raised_or_warn("perform_enqueued_jobs", &block) unless using_test_adapter?

Expand Down Expand Up @@ -646,6 +662,12 @@ def queue_adapter
end

private
def require_active_job_test_adapter!(method)
unless using_test_adapter?
raise ArgumentError.new("#{method} requires the Active Job test adapter, you're using #{queue_adapter.class.name}.")
end
end

def using_test_adapter?
queue_adapter.is_a?(ActiveJob::QueueAdapters::TestAdapter)
end
Expand Down
20 changes: 11 additions & 9 deletions activejob/test/cases/instrumentation_test.rb
Expand Up @@ -23,18 +23,20 @@ class InstrumentationTest < ActiveSupport::TestCase
assert_equal 1, events.size
end

test "retry emits an enqueue retry event" do
events = subscribed("enqueue_retry.active_job") do
perform_enqueued_jobs { RetryJob.perform_later("DefaultsError", 2) }
unless adapter_is?(:inline, :sneakers)
test "retry emits an enqueue retry event" do
events = subscribed("enqueue_retry.active_job") do
perform_enqueued_jobs { RetryJob.perform_later("DefaultsError", 2) }
end
assert_equal 1, events.size
end
assert_equal 1, events.size
end

test "retry exhaustion emits a retry_stopped event" do
events = subscribed("retry_stopped.active_job") do
perform_enqueued_jobs { RetryJob.perform_later("CustomCatchError", 6) }
test "retry exhaustion emits a retry_stopped event" do
events = subscribed("retry_stopped.active_job") do
perform_enqueued_jobs { RetryJob.perform_later("CustomCatchError", 6) }
end
assert_equal 1, events.size
end
assert_equal 1, events.size
end

test "discard emits a discard event" do
Expand Down
64 changes: 34 additions & 30 deletions activejob/test/cases/logging_test.rb
Expand Up @@ -213,14 +213,14 @@ def test_perform_nested_jobs_logging
end
end

def test_enqueue_at_job_logging
events = subscribed { HelloJob.set(wait_until: 24.hours.from_now).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
assert_equal(1, events.count)
key, * = events.first
assert_equal("enqueue_at.active_job", key)
rescue NotImplementedError
skip
unless adapter_is?(:inline, :sneakers)
def test_enqueue_at_job_logging
events = subscribed { HelloJob.set(wait_until: 24.hours.from_now).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
assert_equal(1, events.count)
key, * = events.first
assert_equal("enqueue_at.active_job", key)
end
end

def test_enqueue_at_job_log_error_when_callback_chain_is_halted
Expand All @@ -244,14 +244,14 @@ def test_enqueue_at_job_log_error_when_error_is_raised_during_callback_chain
assert_equal("enqueue_at.active_job", key)
end

def test_enqueue_in_job_logging
events = subscribed { HelloJob.set(wait: 2.seconds).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
assert_equal(1, events.count)
key, * = events.first
assert_equal("enqueue_at.active_job", key)
rescue NotImplementedError
skip
unless adapter_is?(:inline, :sneakers)
def test_enqueue_in_job_logging
events = subscribed { HelloJob.set(wait: 2.seconds).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
assert_equal(1, events.count)
key, * = events.first
assert_equal("enqueue_at.active_job", key)
end
end

def test_enqueue_log_when_enqueue_error_is_set
Expand Down Expand Up @@ -290,10 +290,12 @@ def test_job_no_error_logging_on_rescuable_job
assert_no_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: ArgumentError \(Hair too good\):\n.*\brescue_job\.rb:\d+:in .*perform'/, @logger.messages)
end

def test_enqueue_retry_logging
perform_enqueued_jobs do
RetryJob.perform_later "DefaultsError", 2
assert_match(/Retrying RetryJob \(Job ID: .*?\) after \d+ attempts in 3 seconds, due to a DefaultsError.*\./, @logger.messages)
unless adapter_is?(:inline, :sneakers)
def test_enqueue_retry_logging
perform_enqueued_jobs do
RetryJob.perform_later "DefaultsError", 2
assert_match(/Retrying RetryJob \(Job ID: .*?\) after \d+ attempts in 3 seconds, due to a DefaultsError.*\./, @logger.messages)
end
end
end

Expand All @@ -302,18 +304,20 @@ def test_enqueue_retry_logging_on_retry_job
assert_match(/Retrying RescueJob \(Job ID: .*?\) after \d+ attempts in 0 seconds\./, @logger.messages)
end

def test_retry_stopped_logging
perform_enqueued_jobs do
RetryJob.perform_later "CustomCatchError", 6
unless adapter_is?(:inline, :sneakers)
def test_retry_stopped_logging
perform_enqueued_jobs do
RetryJob.perform_later "CustomCatchError", 6
end
assert_match(/Stopped retrying RetryJob \(Job ID: .*?\) due to a CustomCatchError.*, which reoccurred on \d+ attempts\./, @logger.messages)
end
assert_match(/Stopped retrying RetryJob \(Job ID: .*?\) due to a CustomCatchError.*, which reoccurred on \d+ attempts\./, @logger.messages)
end

def test_retry_stopped_logging_without_block
perform_enqueued_jobs do
RetryJob.perform_later "DefaultsError", 6
rescue DefaultsError
assert_match(/Stopped retrying RetryJob \(Job ID: .*?\) due to a DefaultsError.*, which reoccurred on \d+ attempts\./, @logger.messages)
def test_retry_stopped_logging_without_block
perform_enqueued_jobs do
RetryJob.perform_later "DefaultsError", 6
rescue DefaultsError
assert_match(/Stopped retrying RetryJob \(Job ID: .*?\) due to a DefaultsError.*, which reoccurred on \d+ attempts\./, @logger.messages)
end
end
end

Expand Down
13 changes: 13 additions & 0 deletions activejob/test/cases/queue_adapter_test.rb
Expand Up @@ -53,6 +53,19 @@ class QueueAdapterTest < ActiveJob::TestCase
assert_equal base_queue_adapter, child_job_three.queue_adapter, "child_job_three's queue adapter should remain unchanged"
end

test "should default to :async adapter if no adapters are set at all" do
ActiveJob::Base.disable_test_adapter
_queue_adapter_was = ActiveJob::Base._queue_adapter
_queue_adapter_name_was = ActiveJob::Base._queue_adapter_name
ActiveJob::Base._queue_adapter = ActiveJob::Base._queue_adapter_name = nil

assert_equal "async", ActiveJob::Base.queue_adapter_name
assert_kind_of ActiveJob::QueueAdapters::AsyncAdapter, ActiveJob::Base.queue_adapter
ensure
ActiveJob::Base._queue_adapter = _queue_adapter_was
ActiveJob::Base._queue_adapter_name = _queue_adapter_name_was
end

test "should extract a reasonable name from a class instance" do
child_job = Class.new(ActiveJob::Base)
child_job.queue_adapter = ActiveJob::QueueAdapters::StubOneAdapter.new
Expand Down
31 changes: 29 additions & 2 deletions activejob/test/cases/test_case_test.rb
Expand Up @@ -20,10 +20,37 @@ def test_include_helper
end

def test_set_test_adapter
assert_kind_of ActiveJob::QueueAdapters::TestAdapter, queue_adapter
# The queue adapter the job uses depends on the Active Job config.
# See https://github.com/rails/rails/pull/48585 for logic.
expected = case ActiveJob::Base.queue_adapter_name.to_sym
when :test
ActiveJob::QueueAdapters::TestAdapter
when :inline
ActiveJob::QueueAdapters::InlineAdapter
when :async
ActiveJob::QueueAdapters::AsyncAdapter
when :backburner
ActiveJob::QueueAdapters::BackburnerAdapter
when :delayed_job
ActiveJob::QueueAdapters::DelayedJobAdapter
when :queue_classic
ActiveJob::QueueAdapters::QueueClassicAdapter
when :resque
ActiveJob::QueueAdapters::ResqueAdapter
when :sidekiq
ActiveJob::QueueAdapters::SidekiqAdapter
when :sneakers
ActiveJob::QueueAdapters::SneakersAdapter
when :sucker_punch
ActiveJob::QueueAdapters::SuckerPunchAdapter
else
raise NotImplementedError.new
end

assert_kind_of expected, queue_adapter
end

def test_does_not_perform_enqueued_jobs_by_default
assert_nil queue_adapter.perform_enqueued_jobs
assert_nil ActiveJob::QueueAdapters::TestAdapter.new.perform_enqueued_jobs
end
end

0 comments on commit 80fa112

Please sign in to comment.