如何在resque-retry和Rails 4中测试重试和失败?

时间:2016-08-28 23:57:02

标签: ruby-on-rails ruby rspec resque resque-retry

我正在尝试编写一个测试resque-retry重试功能的规范,我似乎无法让测试正确地命中binding.pry。有没有办法使用rspec 3测试此功能,以便我可以验证它们是否按预期运行?

这是一个请求规范,我试图通过灯具模拟实时请求,但无论我尝试什么,我似乎都无法重试。

if (Math.sqrt(number) % 1 == 0)

我正在使用resque_rspec,并试用此testing strategy

部分规范

gem 'resque', require: 'resque/server'
gem 'resque-web', require: 'resque_web'
gem 'resque-scheduler'
gem 'resque-retry'
gem 'resque-lock-timeout'

队列作业

it 'retries it' do
  stub_request(:any, /.*api.bigcartel.*/).to_return(body: '{}', status: 200)
  @order_shipped_json['order']['originator_id'] = @provider_order
  post "/hook/shops/#{@shop.id}", @order_shipped_json.to_json, format: :json
  ResqueSpec.perform_all(queue_name)
  ???
end

队列作业模块

class QueueHook
  extend Resque::Plugins::LockTimeout
  extend Resque::Plugins::Retry
  extend QueueLock
  extend QueueLogger

  @queue = AppSettings.queues[:hook_queue_name].to_sym
  @lock_timeout = 600
  @retry_exceptions = [QueueError::LockFailed]
  @retry_limit = 600
  @retry_delay = 1

  class << self
    def perform(web_hook_payload_id, _whiplash_customer_id)
      ActiveRecord::Base.clear_active_connections!
      @web_hook_payload = WebHookPayload.find(web_hook_payload_id)
      klass_constructor
      @hook.process_event
    end

    def identifier(_web_hook_payload_id, whiplash_customer_id)
      "lock:integration_hook:#{whiplash_customer_id}"
    end

    def after_perform_delete_webhook(_web_hook_payload_id, _whiplash_customer_id)
      @web_hook_payload.destroy
    end

    private

    ...
  end
end

2 个答案:

答案 0 :(得分:2)

一些注意事项 -

1)正如其他人所提到的,您可能希望将resque回调与其功能分开。也就是说,测试retries正在触发,还要单独测试它们是否按预期运行。您可能希望将它们分成两个单独的测试。

2)为了检查他们是否正在开火,我认为您正在寻找RSpec 3中的class doubles

您需要实例化double,然后引发异常(docs)。这样您就可以查看是否正在调用retries,以及调用它们的次数(docs)。

所以,例如,

it "retries on exception n number of times" do
  queue_hook = class_double("QueueHook")
  expect(queue_hook).to have_received(:on_failure_log_job).exactly(n).times
  allow(queue_hook).to receive(:perform).and_raise(ExceptionClass, "Exception message")
  queue_hook.perform(payload_id, customer_id)
end

还有一点点,所以我无法在本地实施,但希望这可以帮助你朝着正确的方向前进。

答案 1 :(得分:1)

因此,您要测试重试的特定故障来自您实施的此挂钩。

def lock_failed(*)
  raise QueueError::LockFailed
end

我们需要触发这个。 Here是插件中使用的地方。由于您正在使用锁定超时,因此我们希望存根.acquire_lock_algorithm!。这很危险,因为这种方法是插件内部api的一部分。升级插件时请记住这一点。

it 'retries it' do
  stub_request(:any, /.*api.bigcartel.*/).to_return(body: '{}', status: 200)

  allow(QueueHook).to receive(:acquire_lock_algorithm!).and_return(false, true)

  @order_shipped_json['order']['originator_id'] = @provider_order
  post "/hook/shops/#{@shop.id}", @order_shipped_json.to_json, format: :json

  ResqueSpec.perform_all(queue_name)
end

此规范现在应该失败Failure/Error: raise QueueError::LockFailed。由于这是预期的,我们可以设定一个期望。

it 'retries it' do
  stub_request(:any, /.*api.bigcartel.*/).to_return(body: '{}', status: 200)

  allow(QueueHook).to receive(:acquire_lock_algorithm!).and_return(false, true)

  @order_shipped_json['order']['originator_id'] = @provider_order
  post "/hook/shops/#{@shop.id}", @order_shipped_json.to_json, format: :json

  expect {
    ResqueSpec.perform_all(queue_name)
  }.to raise_error(QueueError::LockFailed)
end

除非您设置ResqueSpec.inline = true,否则现在应该传递规范。如果您已为此规范将其设置为false。它会更容易理解。

如果resque-retry正在运行,则作业的失败应该导致作业重新入队到ResqueSpec。我们可以为此添加一个期望。 expect(ResqueSpec.queues[queue_name]).to be_present。不是我们可以再次运营这些工作。我们嘲笑acquire_lock_algorithm!的第二个返回值是真的,所以这次工作应该成功。

由于我们想测试计数器,可以为它们添加阅读器

module QueueLogger
  attr_reader :all_attempts, :num_attempts
end

然后完成规范...

it 'retries it' do
  stub_request(:any, /.*api.bigcartel.*/).to_return(body: '{}', status: 200)

  allow(QueueHook).to receive(:acquire_lock_algorithm!).and_return(false, true)

  @order_shipped_json['order']['originator_id'] = @provider_order
  post "/hook/shops/#{@shop.id}", @order_shipped_json.to_json, format: :json

  # Failing
  expect {
    ResqueSpec.perform_all(queue_name)
  }.to raise_error(QueueError::LockFailed)
  expect(ResqueSpec.queues[queue_name]).to be_present

  # Retrying
  ResqueSpec.perform_all(queue_name)
  expect(QueueHook.num_attempts).to eq(2)
  ... # Whatever else you want to test.
end

如果您想要专门测试日志记录,请将其存根并设置对所调用内容的期望。应该这样做,我有一个在我自己的机器上运行的简化版本。如果没有,我们可能需要了解您的测试和Resque配置的详细信息。