在我的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配置中优雅地做到这一点?
答案 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)
另一种方法是使用APICache作为HTTP库的代理,因为它会代表您处理速率限制。
APICache.get("my_albums", period => 1) do
FlickrRb.get_all_sets
end
当您尝试更频繁地调用API时,这会引发APICache::CannotFetch
。