1

在我的 Rails 项目中,我使用VCR和 RSpec 来测试与外部 REST Web 服务的 HTTP 交互,该服务只允许每秒调用一次

到目前为止,这意味着我最终会运行我的测试套件,直到它由于来自 Web 服务的“超出调用次数”错误而失败。不过,在那个阶段,至少有一些磁带被记录下来,所以我只是不断地运行测试套件,直到最终我将它们全部记录下来,并且套件只能使用磁带运行(我的default_cassette_options = { record: :new_episodes })。这似乎不是一种最佳的做事方式,特别是如果我发现我以后需要经常重新录制我的磁带,并且我担心不断的呼叫可能会使我进入网络服务的黑名单(没有测试他们有我知道的服务器)。

因此,我最终尝试在调用 Web 服务之前直接将调用sleep(1)放入我的 Rspecit块中,然后将这些调用重构到 VCR 配置中:

规范/支持/vcr.rb

VCR.configure do |c|
  # ...
  c.after_http_request do |request, response|
    sleep(1)
  end
end

虽然这似乎工作正常,但有没有更好的方法来做到这一点?目前,如果调用没有磁带的外部服务已经是套件中的最终测试,那么套件会不必要地休眠 1 秒。同样,如果测试套件中没有磁带的 2 个 Web 服务调用之间的时间超过一秒,那么就会有另一个不必要的暂停。有没有人制定任何逻辑来测试这些条件,或者有没有办法在 VCR 配置中优雅地做到这一点?

4

2 回答 2

3

首先,我建议不要使用:new_episodes作为您的记录模式。它有它的用途,但默认 ( :once) 通常是您想要的。为了准确起见,您希望将磁带记录为单次通过的 HTTP 请求序列。使用:new_episodes,您可以得到包含 HTTP 交互的磁带,这些交互是相隔几个月录制的,但现在正在一起播放,而真正的 HTTP 服务器可能不会以同样的方式响应。

其次,我鼓励您倾听测试所暴露的痛苦,并找到将大部分测试套件与这些 HTTP 请求分离的方法。你能找到一种方法来让测试只关注客户端,而端到端的验收测试会发出请求吗?如果您将 HTTP 内容包装在一个简单的接口中,则应该很容易用测试替身代替所有其他测试,并且更容易控制您的输入。

不过,这是一个长期的解决方案。在短期内,您可以像这样调整您的 VCR 配置:

VCR.configure do |vcr|
  allow_next_request_at = nil
  filters = [:real?, lambda { |r| URI(r.uri).host == 'my-throttled-api.com' }]

  vcr.after_http_request(*filters) do |request, response|
    allow_next_request_at = Time.now + 1
  end

  vcr.before_http_request(*filters) do |request|
    if allow_next_request_at && Time.now < allow_next_request_at
      sleep(allow_next_request_at - Time.now)
    end
  end
end

这使用钩子过滤器(如文档所述)仅在对 API 主机的真实请求上运行钩子。allow_next_request_at用于睡眠所需的最短时间。

于 2012-10-11T04:11:45.513 回答
3

另一种方法可能是使用 APICache 作为 HTTP 库周围的代理,因为它将代表您处理速率限制。

APICache.get("my_albums", period => 1) do
  FlickrRb.get_all_sets
end

APICache::CannotFetch当您尝试调用 API 的频率超过您的限制时,就会出现这种情况。

这是APICache Github 存储库的链接

于 2012-10-11T23:14:07.460 回答