1

在我正在进行的项目中,我们使用 VCR 来存储本地和外部服务的磁带。本地的是不断修改的微服务,而外部的几乎没有修改。

由于这个原因,再加上外部服务需要很长时间才能重新录制,这对我们大部分时间只重新录制本地磁带是有意义的。

为了解决这个问题,我们尝试在不同的文件夹(cassettes/localhost 和cassettes/external/sample.com)中分离磁带。

然后我们想出了:

VCR.configure do |config|
  config.around_http_request do |request|
    host = URI(request.uri).host
    vcr_name = VCR.current_cassette.name
    folder = host
    folder = "external/#{folder}" if host != 'localhost'
    VCR.use_cassette("#{folder}/#{vcr_name}", &request)
  end
  [...]
end

但问题是我们有一些测试需要重复请求(完全相同的请求),服务器返回不同的结果。因此,使用上面的代码可以为每个 http 调用重置磁带。第一个请求被记录下来,第二个是第一个请求的回放,即使预期的响应会有所不同。

然后我们尝试了一种使用标签和嵌套磁带的不同方法:

RSpec.configure do |config|
  config.around(:each) do |spec|
    name = spec.metadata[:full_description]
    VCR.use_cassette "external/#{name}", tag: :external do
      VCR.use_cassette "local/#{name}", tag: :internal do
        spec.call
      end
    end
  end
  [...]
end

VCR.configure do |config|
  config.before_record(:external) do |i|
    i.ignore! if URI(i.request.uri).host == 'localhost'
  end

  config.before_record(:internal) do |i|
    i.ignore! if URI(i.request.uri).host != 'localhost'
  end
  [...]
end

但这也行不通。结果是所有 localhost 请求都记录在内部磁带上。VCR 忽略了其余的请求。

那么你有什么建议来解决这个问题吗?

4

2 回答 2

1

然后我们尝试了一种使用标签和嵌套磁带的不同方法……但这也行不通。

是的,我在设计盒式嵌套时并没有考虑到这种用例。HTTP 交互总是记录到最里面的磁带,但可以从任何嵌套级别播放(它首先尝试最里面的磁带,然后向上搜索父链)。我想到的嵌套盒式磁带的主要用例是黄瓜:您可能希望在整个场景中使用单个盒式磁带,但随后您可能希望将特定盒式磁带用于单个步骤定义(即,对于任何使用那一步)。内盒在使用时“接管”,但外盒在弹出内盒时仍然可用。

这是一个有趣的用例,不过……如果您认为 VCR 应该以这种方式工作,请考虑为它打开一个 github 问题,我们可以进行更多讨论。

至于你原来的问题:我认为@operand 的回答会奏效。

于 2012-11-16T23:16:47.843 回答
1

我认为您想研究使用 :match_requests_on 设置。在此处阅读文档:https ://www.relishapp.com/myronmarston/vcr/v/2-3-0/docs/request-matching

这应该允许您记录对同一个 URL 的多个请求,但让它们按顺序重放。

除此之外,我认为您将磁带分成不同目录的方法听起来不错。我过去为强制重新录制特定磁带所做的一件事就是在重新运行规范之前删除磁带本身。在您的情况下,这应该很容易,因为您已经很好地将它们分开了。

代替或除此之外,当您知道这是一个本地请求时,您可以使用 :all 记录设置,并将其修补到您的配置块中。就像是:

VCR.configure do |config|
  config.around_http_request do |request|
    host = URI(request.uri).host
    vcr_name = VCR.current_cassette.name
    folder = host
    if host != 'localhost'
      folder = "external/#{folder}"
      record_mode = :once
    else
      record_mode = :all
    end
    VCR.use_cassette("#{folder}/#{vcr_name}", :record => record_mode, &request)
  end
  [...]
end

请注意,我还没有测试过这个,所以请仔细检查我。当然,当您只想回放内容时,您也不想使用 :all 记录设置。也许您可以在调用测试时以某种方式开发开关。

这不是一个完整的答案,但我希望这会有所帮助。

于 2012-11-14T18:46:31.517 回答