我希望我的后台作业能够内联运行某些标记的测试。 我可以通过用
perform_enqueued do
包装测试来做到这一点,但我希望能够用元数据标记它们,并且如果可能的话,它会自动发生。
我尝试过以下方法:
it "does everything in the job too", perform_enqueued: true do
end
config.around(:each) do |example|
if example.metadata[:perform_enqueued]
perform_enqueued_jobs do
example.run
end
end
end
但它会导致错误:
undefined method `perform_enqueued_jobs=' for ActiveJob::QueueAdapters::InlineAdapter:Class
在你的
spec/rails_helper.rb
:
RSpec.configure do |config|
# ...
config.include ActiveJob::TestHelper
end
或者在你的测试中:
context "when jobs are executed" do
include ActiveJob::TestHelper
# ...
end
然后在你的测试中:
perform_enqueued_jobs do
example.run
end
您需要将测试适配器设置为
ActiveJob::QueueAdapters::TestAdapter
,它会响应 .perform_enqueued_jobs =
。您可以在您的 spec/rails_helper.rb
文件上执行此操作:
ActiveJob::Base.queue_adapter = :test
InlineAdapter
? — ActiveJob::TestHelper
不会让你的!我发现您正在尝试使用
InlineAdapter
...
我有同样的愿望 - 在我的所有测试中使用
InlineAdapter
。
不幸的是 - 至少对于 RSpec 请求测试(ActionDispatch 集成测试) -
ActiveJob::TestHelper
会自动包含到测试上下文中,并且似乎强制你使用 ActiveJob::QueueAdapters::TestAdapter
而不是 InlineAdapter
。
gems/activejob-7.0.4.3/lib/active_job/test_helper.rb
拥有这一切:
ActiveJob::Base.include(TestQueueAdapter)
def before_setup # :nodoc:
test_adapter = queue_adapter_for_test
queue_adapter_changed_jobs.each do |klass|
klass.enable_test_adapter(test_adapter)
end
clear_enqueued_jobs
clear_performed_jobs
super
end
def after_teardown # :nodoc:
super
queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
end
# Specifies the queue adapter to use with all Active Job test helpers.
#
# Returns an instance of the queue adapter and defaults to
# ActiveJob::QueueAdapters::TestAdapter.
#
# Note: The adapter provided by this method must provide some additional
# methods from those expected of a standard ActiveJob::QueueAdapter
# in order to be used with the active job test helpers. Refer to
# ActiveJob::QueueAdapters::TestAdapter.
def queue_adapter_for_test
ActiveJob::QueueAdapters::TestAdapter.new
end
这会导致它完全忽略您可能拥有的任何
config.active_job.queue_adapter = :inline
配置(因为它覆盖queue_adapter
class_attribute
)。
我什至尝试覆盖
queue_adapter_for_test
:
def queue_adapter_for_test
ActiveJob::QueueAdapters::InlineAdapter.new
end
但是它still不起作用,因为
InlineAdapter
没有定义enqueued_jobs
,我们得到这个:
NoMethodError:
undefined method `enqueued_jobs' for #<ActiveJob::QueueAdapters::InlineAdapter:0x00007f7efcce6580>
Did you mean? enqueue
# gems/3.1.0/gems/activejob-7.0.4.3/lib/active_job/test_helper.rb:9:in `enqueued_jobs'
# gems/3.1.0/gems/activejob-7.0.4.3/lib/active_job/test_helper.rb:641:in `clear_enqueued_jobs'
# gems/3.1.0/gems/activejob-7.0.4.3/lib/active_job/test_helper.rb:46:in `before_setup'
总之...没有支持的使用方式
InlineAdapter
包含 ActiveJob::TestHelper
的任何地方(例如请求测试)。但总有解决方法...
enqueued_jobs
等以避免出现错误假设您正在使用 RSpec,您可以添加一个
spec/support/active_job.rb
来执行此操作:
# Override some things from ActiveJob::TestHelper (which gets included automatically by RSpec in
# request tests) so that we can use InlineAdapter instead of TestAdapter and don't have to manually
# call perform_enqueued_jobs any time we have jobs that get enqueued — InlineAdapter automatically
# runs the job immediately (synchronously). See
# https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html.
module UseInlineQueueAdapter
def queue_adapter_for_test
ActiveJob::QueueAdapters::InlineAdapter.new
end
def enqueued_jobs
if queue_adapter.respond_to?(__callee__)
super
else
[]
end
end
def performed_jobs
if queue_adapter.respond_to?(__callee__)
super
else
[]
end
end
end
RSpec.configure do |config|
config.include UseInlineQueueAdapter
end
就我而言,我很高兴仅使用内联适配器进行所有测试...
但听起来您希望能够仅将其用于一些测试,并用元数据进行标记。
看起来这应该不会更难。看起来您所要做的就是向您重写的
queue_adapter_for_test
方法添加一个条件:
def queue_adapter_for_test
if example.metadata[:inline_jobs]
ActiveJob::QueueAdapters::InlineAdapter.new
else
ActiveJob::QueueAdapters::TestAdapter.new
end
end
TestAdapter
就像它希望你那样由于
perform_enqueued_jobs
会导致在块持续时间内排队的任何作业立即执行(“内联”),因此您所做的也应该可以正常工作 - 但前提是您将 queue_adapter
设置为 TestAdapter
(:test
)
):
config.around(:each) do |example|
if example.metadata[:perform_enqueued]
perform_enqueued_jobs do
example.run
end
end
end
它对你不起作用的另一个原因是因为
around(:each)
在before(:each)
之前运行。因此,即使
ActiveJob::TestHelper
自动将队列适配器更改为
TestAdapter
— 它是通过
before(:each)
(技术上来说是
before_setup
回调)来实现的。因此,当您的
around(:each)
调用
perform_enqueued_jobs
时,
ActiveJob::Base.queue_adapter
应该仍然是在您的配置中配置的。想必您的
config.active_job.queue_adapter = :inline
中有类似
config/environments/test.rb
的内容?正如其他答案所指出的,如果您希望自己的方法发挥作用,则必须将其更改为
:test
。因为,正如错误指出的那样,
InlineAdapter
没有
enqueued_jobs
的概念——相应地,也没有定义名为
perform_enqueued_jobs
的方法。这是我想出的方法,似乎有效:
RSpec.configure do |config|
config.include(Module.new do
# Without this, the perform_enqueued_jobs block below has no effect, because it sets
# perform_enqueued_jobs on ActiveJob::Base.queue_adapter, yet
# ActiveJob::TestHelper#queue_adapter_for_test by default instantiates a _new_
# ActiveJob::QueueAdapters::TestAdapter.new (this happens in a before(:example)), whose
# perform_enqueued_jobs attribute would of course still have the default value of nil.
def queue_adapter_for_test
if ActiveJob::Base.queue_adapter.is_a?(ActiveJob::QueueAdapters::TestAdapter)
ActiveJob::Base.queue_adapter
else
super
end
end
end)
config.around do |example|
if example.metadata[:perform_enqueued_jobs]
perform_enqueued_jobs do
example.run
end
else
example.run
end
end
end
现在您可以继续注释任何示例组或您希望立即执行排队作业的示例!
it "performs the job immediately as soon as enqueued", :perform_enqueued_jobs do
# ...
end
在尝试了这两种解决方法之后,我现在推荐第二种,因为它更灵活,并且仍然允许您在任何需要它的测试中进行异步排队,但是当您可以摆脱更简单的情况时,可以切换到内联执行选项...
ActiveJob::TestHelper
干扰了在
ActiveJob::Base
上设置队列适配器。事实证明,该模块将以下内容添加到
ActiveJob::Base
:
module ClassMethods
def queue_adapter
self._test_adapter.nil? ? super : self._test_adapter
end
def disable_test_adapter
self._test_adapter = nil
end
def enable_test_adapter(test_adapter)
self._test_adapter = test_adapter
end
end
我最终需要的是以下内容,看起来 ActiveJob::TestHelper
处理清理工作:
describe 'something that needs to queue real jobs' do
before do
ActiveJob::Base.enable_test_adapter ActiveJob::QueueAdapters::GoodJobAdapter.new(execution_mode: :async)
end
# Test stuff that has been queued via the GoodJob adapter
end