ruby-on-rails rspec rails测试:如何强制ActiveJob作业内联运行某些测试?

sqserrrh  于 2023-03-31  发布在  Ruby
关注(0)|答案(3)|浏览(150)

我希望我的后台作业可以为某些标记的测试内联运行。我可以通过用perform_enqueued do Package 测试来实现,但我希望能够用元数据标记它们,如果可能的话,它会自动发生。
我尝试了以下方法:

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
mi7gmzs6

mi7gmzs61#

您需要将用于测试的适配器设置为ActiveJob::QueueAdapters::TestAdapter,以响应.perform_enqueued_jobs =。您可以在spec/rails_helper.rb文件中这样做:

ActiveJob::Base.queue_adapter = :test
ndasle7k

ndasle7k2#

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
5hcedyr0

5hcedyr03#

如何在测试中使用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_adapterclass_attribute)。
我什至尝试覆盖queue_adapter_for_test

def queue_adapter_for_test
      ActiveJob::QueueAdapters::InlineAdapter.new
    end

但是它 * 仍然 * 不起作用,因为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'

总而言之...在包含ActiveJob::TestHelper的任何地方(例如请求测试),都没有 * 支持 * 的方式使用InlineAdapter。但总是有解决方案...

解决方案一:覆盖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)之前运行。(技术上是before_setup回调)。因此,在您的around(:each)调用perform_enqueued_jobs时,ActiveJob::Base.queue_adapter应该仍然是您在配置中配置的。
假设你的config/environments/test.rb中有类似config.active_job.queue_adapter = :inline的东西?正如其他答案所指出的,如果你想让你的方法工作,你必须把它改为: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

在尝试了这两种解决方法之后,我现在推荐第二种,因为它更灵活,并且仍然允许您在任何需要它的测试中进行异步排队,但是当您可以使用更简单的选项时,可以切换到内联执行。

相关问题