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

Chr*_*ugh 19 ruby rspec ruby-on-rails resque resque-retry

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

这是一个请求规范,我试图通过fixtures模拟一个实时请求,但无论我尝试什么,我似乎无法得到重试的工作.

gem 'resque', require: 'resque/server'
gem 'resque-web', require: 'resque_web'
gem 'resque-scheduler'
gem 'resque-retry'
gem 'resque-lock-timeout'
Run Code Online (Sandbox Code Playgroud)

我正在使用resque_rspec,并尝试此测试策略.

部分规格

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
Run Code Online (Sandbox Code Playgroud)

队列工作

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
Run Code Online (Sandbox Code Playgroud)

队列作业模块

module QueueLogger
  def before_perform_log_job(*args)
    Rails.logger.info "[Resque][#{self}] running with #{args.inspect}..."
  end

  def on_failure_log_job(*args)
    message = "[Resque][#{self}] failed with #{args.inspect}..."
    run_counters
    Rails.logger.info message_builder(message)
  end

  private

  def run_counters
    @num_attempts += retry_attempt
    @all_attempts += retry_limit
  end

  def message_builder(message)
    return message unless @num_attempts
    return message += " Retrying (attempt ##{@num_attempts + 1})" if @num_attempts < @all_attempts
    message += ' Giving up.'
    message
  end
end

module QueueLock
  def loner_enqueue_failed(*args)
    Rails.logger.info "[Resque][#{self}] is already enqueued: #{args.inspect}..."
  end

  def lock_failed(*)
    raise QueueError::LockFailed
  end
end
Run Code Online (Sandbox Code Playgroud)

小智 1

因此,您想要测试重试的特定失败来自于您实现的这个钩子。

def lock_failed(*)
  raise QueueError::LockFailed
end
Run Code Online (Sandbox Code Playgroud)

我们需要触发这个。是它在插件中使用的地方。由于您使用了锁定超时,看起来我们想要存根.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
Run Code Online (Sandbox Code Playgroud)

这个规范现在应该失败了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
Run Code Online (Sandbox Code Playgroud)

除非您已设置,否则规范现在应该已通过ResqueSpec.inline = true。如果您已针对此规范将其设置为 false。它将更容易遵循。

如果 resque-retry 正在工作,那么作业失败应该会导致作业重新排队到 ResqueSpec。我们可以为此添加一个期望。expect(ResqueSpec.queues[queue_name]).to be_present。不是我们可以再次运行作业。我们将第二个返回值模拟acquire_lock_algorithm!为 true,因此这次作业应该会成功。

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

module QueueLogger
  attr_reader :all_attempts, :num_attempts
end
Run Code Online (Sandbox Code Playgroud)

然后完成规格...

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
Run Code Online (Sandbox Code Playgroud)

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