rspec Rails 测试:如何强制 ActiveJob 作业内联运行某些测试?

问题描述 投票:0回答:4

我希望我的后台作业能够内联运行某些标记的测试。 我可以通过用

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
ruby-on-rails ruby rspec rails-activejob
4个回答
14
投票

在你的

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

12
投票

您需要将测试适配器设置为

ActiveJob::QueueAdapters::TestAdapter
,它会响应
.perform_enqueued_jobs =
。您可以在您的
spec/rails_helper.rb
文件上执行此操作:

ActiveJob::Base.queue_adapter = :test

1
投票

如何在测试中使用
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
的任何地方(例如请求测试)。但总有解决方法...

解决方法#1:覆盖
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

解决方法#2:只需使用
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
在尝试了这两种解决方法之后,我现在推荐第二种,因为它更灵活,并且仍然允许您在任何需要它的测试中进行异步排队,但是当您可以摆脱更简单的情况时,可以切换到内联执行选项...


0
投票
就我而言,我想在异步模式下启用 GoodJob 进行特定测试,以便我可以正确测试用户取消作业时是否被丢弃。我发现

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
    
© www.soinside.com 2019 - 2024. All rights reserved.