使用 VCR 和 RSpec 测试速率受限的外部 API 调用 [英] Testing rate-limited external API calls with VCR and RSpec

查看:30
本文介绍了使用 VCR 和 RSpec 测试速率受限的外部 API 调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my Rails project, I'm using VCR and RSpec to test HTTP interactions against an external REST web service that only allows calls to it once per second.

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

What this means so far is that I end up running my test suite until it fails due to a "number of calls exceeded" error from the web service. At that stage though, at least some cassettes get recorded, so I just continually run the test suite until eventually I get them all recorded and the suite can run using only cassettes (my default_cassette_options = { record: :new_episodes }). This doesn't seem like an optimal way to do things, especially if I find I need to re-record my cassettes in the future often, and I worry that constant calls could land me on a blacklist with the web service (there's no test server they have that I know about).

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

So, I ended up trying putting calls to sleep(1) in my Rspec it blocks directly before the call to the web service is made, and then refactored those calls up into the VCR configuration:

spec/support/vcr.rb

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

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

Although this seems to work fine, is there a better way to do this? At the moment, if a call to an external service that doesn't have a cassette already is the final test in the suite, then the suite sleeps unnecessarily for 1 second. Likewise, if the time between 2 web service calls without cassettes in the test suite is more than once second, then there's another unnecessary pause. Has anyone made any kind of logic to test for these kinds of conditions, or is there a way to elegantly do this in the VCR configuration?

推荐答案

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

First off, I would recommend against using :new_episodes as your record mode. It has it's uses, but the default (:once) is generally what you want. For accuracy, you want to record a cassette as a sequence of HTTP requests that were made in a single pass. With :new_episodes, you can wind up with cassettes that contain HTTP interactions that were recorded months apart but are now being played back together, and the real HTTP server may not respond in that same fashion.

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

Secondly, I'd encourage you to listen to the pain exposed by your tests, and find ways to decouple most of your test suite from these HTTP requests. Can you find a way to make it so that just the tests focused on the client, and the end-to-end acceptance tests make the requests? If you wrap the HTTP stuff in a simple interface, it should be easy to substitute a test double for all the other tests, and more easily control your inputs.

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

That's a longer term fix, though. In the short term, you can tweak your VCR config like so:

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 用于睡眠所需的最短时间.

This uses hook filters (as documented) to run the hooks only on real requests to the API host. allow_next_request_at is used to sleep the minimum amount of time necessary.

这篇关于使用 VCR 和 RSpec 测试速率受限的外部 API 调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆