aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activejob/lib/active_job.rb2
-rw-r--r--activejob/lib/active_job/base.rb3
-rw-r--r--activejob/lib/active_job/queue_adapter.rb33
-rw-r--r--activejob/lib/active_job/queue_adapters.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb77
-rw-r--r--activejob/lib/active_job/test_case.rb103
-rw-r--r--activejob/lib/active_job/test_helper.rb170
-rw-r--r--activejob/test/cases/test_helper_test.rb217
-rw-r--r--activejob/test/cases/test_test.rb43
-rw-r--r--railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb4
10 files changed, 637 insertions, 16 deletions
diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb
index 29123170b8..93d9d1b2d8 100644
--- a/activejob/lib/active_job.rb
+++ b/activejob/lib/active_job.rb
@@ -31,4 +31,6 @@ module ActiveJob
autoload :Base
autoload :QueueAdapters
+ autoload :TestCase
+ autoload :TestHelper
end
diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb
index d5ba253016..1b54786303 100644
--- a/activejob/lib/active_job/base.rb
+++ b/activejob/lib/active_job/base.rb
@@ -8,8 +8,7 @@ require 'active_job/logging'
module ActiveJob
class Base
- extend QueueAdapter
-
+ include QueueAdapter
include QueueName
include Enqueuing
include Execution
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 13c23abce4..c597093458 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -3,21 +3,28 @@ require 'active_support/core_ext/string/inflections'
module ActiveJob
module QueueAdapter
- mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
+ extend ActiveSupport::Concern
- def queue_adapter=(name_or_adapter)
- @@queue_adapter = \
- case name_or_adapter
- when Symbol, String
- load_adapter(name_or_adapter)
- when Class
- name_or_adapter
- end
- end
+ module ClassMethods
+ delegate :performed_jobs, :performed_jobs=,
+ :enqueued_jobs, :enqueued_jobs=,
+ to: ActiveJob::QueueAdapters::TestAdapter
+ mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
- private
- def load_adapter(name)
- "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
+ def queue_adapter=(name_or_adapter)
+ @@queue_adapter = \
+ case name_or_adapter
+ when Symbol, String
+ load_adapter(name_or_adapter)
+ when Class
+ name_or_adapter
+ end
end
+
+ private
+ def load_adapter(name)
+ "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
+ end
+ end
end
end \ No newline at end of file
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 007068ff0a..345b01ef00 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -12,5 +12,6 @@ module ActiveJob
autoload :SidekiqAdapter
autoload :SneakersAdapter
autoload :SuckerPunchAdapter
+ autoload :TestAdapter
end
end
diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb
new file mode 100644
index 0000000000..f7c4d19638
--- /dev/null
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -0,0 +1,77 @@
+module ActiveJob
+ module QueueAdapters
+ class TestAdapter
+ mattr_accessor(:perform_enqueued_jobs) { false }
+ mattr_accessor(:perform_enqueued_at_jobs) { false }
+
+ class << self
+ # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
+ def enqueued_jobs
+ @@enqueued_jobs ||= []
+ end
+
+ # Allows you to over write the default enqueued jobs store from an array to some
+ # other object. If you just want to clear the store,
+ # call ActiveJob::QueueAdapters::TestAdapter.enqueued_jobs.clear.
+ #
+ # If you place another object here, please make sure it responds to:
+ #
+ # * << (message)
+ # * clear
+ # * length
+ # * size
+ # * and other common Array methods
+ def enqueued_jobs=(val)
+ @@enqueued_jobs = val
+ end
+
+ # Provides a store of all the performed jobs with the TestAdapter so you can check them.
+ def performed_jobs
+ @@performed_jobs ||= []
+ end
+
+ # Allows you to over write the default performed jobs store from an array to some
+ # other object. If you just want to clear the store,
+ # call ActiveJob::QueueAdapters::TestAdapter.performed_jobs.clear.
+ #
+ # If you place another object here, please make sure it responds to:
+ #
+ # * << (message)
+ # * clear
+ # * length
+ # * size
+ # * and other common Array methods
+ def performed_jobs=(val)
+ @@performed_jobs = val
+ end
+
+ def enqueue(job, *args)
+ if perform_enqueued_jobs?
+ performed_jobs << {job: job, args: args, queue: job.queue_name}
+ job.new.execute(*args)
+ else
+ enqueued_jobs << {job: job, args: args, queue: job.queue_name}
+ end
+ end
+
+ def enqueue_at(job, timestamp, *args)
+ if perform_enqueued_at_jobs?
+ performed_jobs << {job: job, args: args, queue: job.queue_name, run_at: timestamp}
+ job.new.execute(*args)
+ else
+ enqueued_jobs << {job: job, args: args, queue: job.queue_name, run_at: timestamp}
+ end
+ end
+
+ private
+ def perform_enqueued_jobs?
+ perform_enqueued_jobs
+ end
+
+ def perform_enqueued_at_jobs?
+ perform_enqueued_at_jobs
+ end
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/test_case.rb b/activejob/lib/active_job/test_case.rb
new file mode 100644
index 0000000000..0b76fdb5a7
--- /dev/null
+++ b/activejob/lib/active_job/test_case.rb
@@ -0,0 +1,103 @@
+# encoding: utf-8
+require 'active_support/test_case'
+
+module ActiveJob
+ class NonInferrableJobError < ::StandardError
+ def initialize(name)
+ super "Unable to determine the job to test from #{name}. " \
+ "You'll need to specify it using 'tests YourJob' in your " \
+ 'test case definition'
+ end
+ end
+
+ class TestCase < ActiveSupport::TestCase
+ module Behavior
+ extend ActiveSupport::Concern
+
+ include ActiveSupport::Testing::ConstantLookup
+ include ActiveJob::TestHelper
+
+ included do
+ class_attribute :_job_class
+ setup :initialize_test_adapter
+ teardown :restore_previous_adapter
+ end
+
+ module ClassMethods
+ def tests(job)
+ case job
+ when String, Symbol
+ self._job_class = job.to_s.camelize.constantize
+ when Module
+ self._job_class = job
+ else
+ fail NonInferrableJobError.new(job)
+ end
+ end
+
+ def job_class
+ if job = _job_class
+ job
+ else
+ tests determine_default_job(name)
+ end
+ end
+
+ def determine_default_job(name)
+ job = determine_constant_from_test_name(name) do |constant|
+ Class === constant && constant < ActiveJob::Base
+ end
+ fail NonInferrableJobError.new(name) if job.nil?
+ job
+ end
+ end
+
+ protected
+ def initialize_test_adapter
+ @old_adapter = ActiveJob::Base.queue_adapter
+ ActiveJob::Base.queue_adapter = :test
+ save_test_adapter_behavior
+ end
+
+ def save_test_adapter_behavior
+ @old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
+ @old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
+ end
+
+ def restore_test_adapter_behavior
+ queue_adapter.perform_enqueued_jobs = @old_perform_enqueued_jobs
+ queue_adapter.perform_enqueued_at_jobs = @old_perform_enqueued_at_jobs
+ end
+
+ def restore_previous_adapter
+ restore_test_adapter_behavior
+ ActiveJob::Base.queue_adapter = @old_adapter
+ ActiveJob::Base.performed_jobs.clear
+ ActiveJob::Base.enqueued_jobs.clear
+ end
+
+ def perform_enqueued_jobs
+ queue_adapter.perform_enqueued_jobs = true
+ end
+
+ def perform_enqueued_at_jobs
+ queue_adapter.perform_enqueued_at_jobs = true
+ end
+
+ def enqueue_jobs
+ queue_adapter.perform_enqueued_jobs = false
+ end
+
+ def enqueue_at_jobs
+ queue_adapter.perform_enqueued_at_jobs = false
+ end
+
+ private
+ def queue_adapter
+ ActiveJob::Base.queue_adapter
+ end
+ end
+
+ include Behavior
+ end
+end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
new file mode 100644
index 0000000000..d147e56709
--- /dev/null
+++ b/activejob/lib/active_job/test_helper.rb
@@ -0,0 +1,170 @@
+# encoding: utf-8
+module ActiveJob
+ # Provides helper methods for testing Active Job
+ module TestHelper
+ # Asserts that the number of enqueued jobs matches the given number.
+ #
+ # def test_jobs
+ # assert_enqueued_jobs 0
+ # HelloJob.enqueue('david')
+ # assert_enqueued_jobs 1
+ # HelloJob.enqueue('abdelkader')
+ # assert_enqueued_jobs 2
+ # end
+ #
+ # If a block is passed, that block should cause the specified number of
+ # jobs to be enqueued.
+ #
+ # def test_jobs_again
+ # assert_enqueued_jobs 1 do
+ # HelloJob.enqueue('cristian')
+ # end
+ #
+ # assert_enqueued_jobs 2 do
+ # HelloJob.enqueue('aaron')
+ # HelloJob.enqueue('rafael')
+ # end
+ # end
+ def assert_enqueued_jobs(number)
+ if block_given?
+ original_count = enqueued_jobs.size
+ yield
+ new_count = enqueued_jobs.size
+ assert_equal original_count + number, new_count,
+ "#{number} job expected, but #{new_count - original_count} were enqueued"
+ else
+ assert_equal number, enqueued_jobs.size
+ end
+ end
+
+ # Assert that no job have been enqueued.
+ #
+ # def test_jobs
+ # assert_no_enqueued_jobs
+ # HelloJob.enqueue('jeremy')
+ # assert_enqueued_jobs 1
+ # end
+ #
+ # If a block is passed, that block should not cause any job to be enqueued.
+ #
+ # def test_jobs_again
+ # assert_no_enqueued_jobs do
+ # # No job should be enqueued from this block
+ # end
+ # end
+ #
+ # Note: This assertion is simply a shortcut for:
+ #
+ # assert_enqueued_jobs 0
+ def assert_no_enqueued_jobs(&block)
+ assert_enqueued_jobs 0, &block
+ end
+
+ # Asserts that the number of performed jobs matches the given number.
+ #
+ # def test_jobs
+ # assert_performed_jobs 0
+ # HelloJob.enqueue('xavier')
+ # assert_performed_jobs 1
+ # HelloJob.enqueue('yves')
+ # assert_performed_jobs 2
+ # end
+ #
+ # If a block is passed, that block should cause the specified number of
+ # jobs to be performed.
+ #
+ # def test_jobs_again
+ # assert_performed_jobs 1 do
+ # HelloJob.enqueue('robin')
+ # end
+ #
+ # assert_performed_jobs 2 do
+ # HelloJob.enqueue('carlos')
+ # HelloJob.enqueue('sean')
+ # end
+ # end
+ def assert_performed_jobs(number)
+ if block_given?
+ original_count = self.class.job_class.performed_jobs.size
+ yield
+ new_count = performed_jobs.size
+ assert_equal original_count + number, new_count,
+ "#{number} job expected, but #{new_count - original_count} were performed"
+ else
+ assert_equal number, performed_jobs.size
+ end
+ end
+
+ # Assert that no job have been performed.
+ #
+ # def test_jobs
+ # assert_no_performed_jobs
+ # HelloJob.enqueue('matthew')
+ # assert_performed_jobs 1
+ # end
+ #
+ # If a block is passed, that block should not cause any job to be performed.
+ #
+ # def test_jobs_again
+ # assert_no_performed_jobs do
+ # # No job should be performed from this block
+ # end
+ # end
+ #
+ # Note: This assertion is simply a shortcut for:
+ #
+ # assert_performed_jobs 0
+ def assert_no_performed_jobs(&block)
+ assert_performed_jobs 0, &block
+ end
+
+ # Assert that a job was enqueued in the block matches the args
+ #
+ # def assert_enqueued_job
+ # assert_enqueued_job(job: MyJob, args: [1,2,3], queue: 'low') do
+ # MyJob.enqueue(1,2,3)
+ # end
+ # end
+ def assert_enqueued_job(args = {}, &_block)
+ original_enqueued_jobs = enqueued_jobs
+ enqueued_jobs.clear
+ args.assert_valid_keys(:job, :args, :at, :queue)
+ yield
+ matching_job = enqueued_jobs.any? do |job|
+ args.all? { |key, value| value == job[key] }
+ end
+ assert matching_job
+ ensure
+ ActiveJob::Base.enqueued_jobs = original_enqueued_jobs + enqueued_jobs
+ end
+
+ # Assert that a job was performed in the block matches the args
+ #
+ # def assert_performed_job
+ # assert_performed_job(job: MyJob, args: [1,2,3], queue: 'high') do
+ # MyJob.enqueue(1,2,3)
+ # end
+ # end
+ def assert_performed_job(args = {}, &_block)
+ original_performed_jobs = performed_jobs
+ performed_jobs.clear
+ args.assert_valid_keys(:job, :args, :at, :queue)
+ yield
+ matching_job = performed_jobs.any? do |job|
+ args.all? { |key, value| value == job[key] }
+ end
+ assert matching_job, "No performed job found with #{args}"
+ ensure
+ ActiveJob::Base.performed_jobs = original_performed_jobs + performed_jobs
+ end
+
+ private
+ def enqueued_jobs
+ ActiveJob::Base.enqueued_jobs
+ end
+
+ def performed_jobs
+ ActiveJob::Base.performed_jobs
+ end
+ end
+end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
new file mode 100644
index 0000000000..b98c8e01a5
--- /dev/null
+++ b/activejob/test/cases/test_helper_test.rb
@@ -0,0 +1,217 @@
+# encoding: utf-8
+require 'helper'
+require 'active_support/core_ext/time'
+require 'active_support/core_ext/date'
+require 'jobs/hello_job'
+require 'jobs/logging_job'
+require 'jobs/nested_job'
+
+class EnqueuedJobsTest < ActiveJob::TestCase
+ tests HelloJob
+ setup :perform_enqueued_at_jobs
+
+ def test_assert_enqueued_jobs
+ assert_nothing_raised do
+ assert_enqueued_jobs 1 do
+ HelloJob.enqueue('david')
+ end
+ end
+ end
+
+ def test_repeated_enqueued_jobs_calls
+ assert_nothing_raised do
+ assert_enqueued_jobs 1 do
+ HelloJob.enqueue('abdelkader')
+ end
+ end
+
+ assert_nothing_raised do
+ assert_enqueued_jobs 2 do
+ HelloJob.enqueue('sean')
+ HelloJob.enqueue('yves')
+ end
+ end
+ end
+
+ def test_assert_enqueued_jobs_with_no_block
+ assert_nothing_raised do
+ HelloJob.enqueue('rafael')
+ assert_enqueued_jobs 1
+ end
+
+ assert_nothing_raised do
+ HelloJob.enqueue('aaron')
+ HelloJob.enqueue('matthew')
+ assert_enqueued_jobs 3
+ end
+ end
+
+ def test_assert_no_enqueued_jobs
+ assert_nothing_raised do
+ assert_no_enqueued_jobs do
+ # Scheduled jobs are being performed in this context
+ HelloJob.enqueue_at(Date.tomorrow.noon, 'godfrey')
+ end
+ end
+ end
+
+ def test_assert_enqueued_jobs_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 2 do
+ HelloJob.enqueue('xavier')
+ end
+ end
+
+ assert_match(/2 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_jobs_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 1 do
+ HelloJob.enqueue('cristian')
+ HelloJob.enqueue('guillermo')
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+ #
+ def test_assert_no_enqueued_jobs_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs do
+ HelloJob.enqueue('jeremy')
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_job
+ assert_enqueued_job(job: LoggingJob, queue: 'default') do
+ NestedJob.enqueue_at(Date.tomorrow.noon)
+ end
+ end
+
+ def test_assert_enqueued_job_failure
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_job(job: LoggingJob, queue: 'default') do
+ NestedJob.enqueue
+ end
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_job(job: NestedJob, queue: 'low') do
+ NestedJob.enqueue
+ end
+ end
+ end
+
+ def test_assert_enqueued_job_args
+ assert_raise ArgumentError do
+ assert_enqueued_job(class: LoggingJob) do
+ NestedJob.enqueue_at(Date.tomorrow.noon)
+ end
+ end
+ end
+end
+
+class PerformedJobsTest < ActiveJob::TestCase
+ tests HelloJob
+ setup :perform_enqueued_jobs
+
+ def test_assert_performed_jobs
+ assert_nothing_raised do
+ assert_performed_jobs 1 do
+ HelloJob.enqueue('david')
+ end
+ end
+ end
+
+ def test_repeated_performed_jobs_calls
+ assert_nothing_raised do
+ assert_performed_jobs 1 do
+ HelloJob.enqueue('abdelkader')
+ end
+ end
+
+ assert_nothing_raised do
+ assert_performed_jobs 2 do
+ HelloJob.enqueue('sean')
+ HelloJob.enqueue('yves')
+ end
+ end
+ end
+
+ def test_assert_performed_jobs_with_no_block
+ assert_nothing_raised do
+ HelloJob.enqueue('rafael')
+ assert_performed_jobs 1
+ end
+
+ assert_nothing_raised do
+ HelloJob.enqueue('aaron')
+ HelloJob.enqueue('matthew')
+ assert_performed_jobs 3
+ end
+ end
+
+ def test_assert_no_performed_jobs
+ assert_nothing_raised do
+ assert_no_performed_jobs do
+ # Scheduled jobs are being enqueued in this context
+ HelloJob.enqueue_at(Date.tomorrow.noon, 'godfrey')
+ end
+ end
+ end
+
+ def test_assert_performed_jobs_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 2 do
+ HelloJob.enqueue('xavier')
+ end
+ end
+
+ assert_match(/2 .* but 1/, error.message)
+ end
+
+ def test_assert_performed_jobs_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1 do
+ HelloJob.enqueue('cristian')
+ HelloJob.enqueue('guillermo')
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+ #
+ def test_assert_no_performed_jobs_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs do
+ HelloJob.enqueue('jeremy')
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_performed_job
+ assert_performed_job(job: NestedJob, queue: 'default') do
+ NestedJob.enqueue
+ end
+ end
+
+ def test_assert_performed_job_failure
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_job(job: LoggingJob, queue: 'default') do
+ NestedJob.enqueue_at(Date.tomorrow.noon)
+ end
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_job(job: NestedJob, queue: 'low') do
+ NestedJob.enqueue_at(Date.tomorrow.noon)
+ end
+ end
+ end
+end
diff --git a/activejob/test/cases/test_test.rb b/activejob/test/cases/test_test.rb
new file mode 100644
index 0000000000..a4d786f3da
--- /dev/null
+++ b/activejob/test/cases/test_test.rb
@@ -0,0 +1,43 @@
+# encoding: utf-8
+require 'helper'
+require 'jobs/hello_job'
+require 'jobs/logging_job'
+require 'jobs/nested_job'
+
+class ActiveJobTestCaseTest < ActiveJob::TestCase
+ tests HelloJob
+
+ def test_set_job_class_manual
+ assert_equal HelloJob, self.class.job_class
+ end
+end
+
+class CrazySymbolNameJobTest < ActiveJob::TestCase
+ tests :hello_job
+
+ def test_set_job_class_manual_using_symbol
+ assert_equal HelloJob, self.class.job_class
+ end
+end
+
+class CrazyStringNameJobTest < ActiveJob::TestCase
+ tests 'hello_job'
+
+ def test_set_job_class_manual_using_string
+ assert_equal HelloJob, self.class.job_class
+ end
+end
+
+class HelloJobTest < ActiveJob::TestCase
+ def test_set_job_class_manual
+ assert_equal HelloJob, self.class.job_class
+ end
+end
+
+class CrazyNameJobTest < ActiveJob::TestCase
+ def test_determine_default_job_raises_correct_error
+ assert_raise(ActiveJob::NonInferrableJobError) do
+ self.class.job_class
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
index 6200218313..5a9f0aded9 100644
--- a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
+++ b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
@@ -1,7 +1,9 @@
require 'test_helper'
<% module_namespacing do -%>
-class <%= class_name %>JobTest < ActiveSupport::TestCase
+class <%= class_name %>JobTest < ActiveJob::TestCase
+ tests <%= class_name %>Job
+
# test "the truth" do
# assert true
# end