使用VCR和RSpec测试速率受限的外部API调用

时间:2012-10-11 00:59:59

标签: ruby-on-rails api rspec rspec2 vcr

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

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

所以,我最终尝试在调用Web服务之前直接在我的Rspec sleep(1)块中调用it,然后将这些调用重构为VCR配置:

规格/支持/ vcr.rb

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

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

2 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:3)

另一种方法是使用API​​Cache作为HTTP库的代理,因为它会代表您处理速率限制。

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

当您尝试更频繁地调用API时,这会引发APICache::CannotFetch

以下是APICache Github repo

的链接