19

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

这是一个请求规范,我正在尝试通过夹具模拟实时请求,但无论我尝试什么,我似乎都无法让工作重试。

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

我正在使用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

排队作业

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

队列作业模块

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
4

2 回答 2

2

几点说明——

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

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

您将需要实例化双精度,然后引发异常(docs)。这将允许您查看您retries是否被调用,以及它们被调用了多少次(文档)。

所以,例如,

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

发生了很多事情,所以我无法在本地实施,但希望这可以帮助您朝着正确的方向前进。

于 2016-09-20T04:19:54.040 回答
1

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

def lock_failed(*)
  raise QueueError::LockFailed
end

我们需要触发它。是它在插件中使用的地方。由于您使用的是锁定超时,因此看起来我们想要 stub .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!为 true,因此这次工作应该会成功。

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

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 配置的详细信息。

于 2016-09-23T03:57:47.807 回答