
测试开发工具【免费下载链接】vcrRecord your test suites HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.项目地址https://gitcode.com/gh_mirrors/vc/vcr点击查看免费下载导读本文围绕 VCR 的:headers请求匹配器展开当同一个 URI 上存在多个互不相同、需要用请求头区分的 HTTP 交互时match_requests_on: [:headers]能让 VCR 依据请求头精确挑选要回放的响应。读完本文你将掌握:headers匹配器的配置方法、底层匹配原理头键大小写规范化与值数组化、与默认匹配器的协作方式以及它在真实测试场景中的完整落地示例。:headers匹配器是什么VCR 的核心能力是录制测试套件的 HTTP 交互并在后续测试运行时回放。回放的关键在于当一次真实请求发生时VCR 必须从 cassette 中找出与当前请求最相似的已录交互这一查找动作由**请求匹配器request matcher**完成。默认情况下VCR 只使用:method和:uri两个匹配器见 lib/vcr/request_matcher_registry.rb 中的DEFAULT_MATCHERS [:method, :uri]。这意味着只要 HTTP 方法和 URI 相同VCR 就会认为两次请求是同一个请求直接回放第一条匹配的交互。但真实业务中同一个 URI 常常承载多种语义同一个 API 端点可能因X-User-Id、Authorization、Accept-Language等请求头的不同返回截然不同的数据。此时:headers匹配器就派上用场——它把请求头集合纳入匹配依据让 VCR 能区分看起来一样、实则不同的请求。该匹配器在源码中的注册非常简单直观见 lib/vcr/request_matcher_registry.rbregister(:headers) { |r1, r2| r1.headers r2.headers }也就是两个请求的 headers 哈希完全相等即匹配成功否则不匹配。完整实战示例按 X-User-Id 区分用户下面是一个可直接运行的完整示例演示如何在 cassette 中录制两个URI 与方法完全相同、仅请求头不同的交互并用:headers匹配器分别回放。该示例同时被 Cucumber 特性文件 features/request_matching/headers.feature 覆盖验证。第一步准备 cassette在cassettes/example.yml中存放两条交互记录二者method都是post、uri都是http://example.net/some/long/path唯一的区别是请求头X-User-Id分别为1和2--- http_interactions: - request: method: post uri: http://example.net/some/long/path body: encoding: UTF-8 string: headers: X-User-Id: - 1 response: status: code: 200 message: OK headers: Content-Length: - 15 body: encoding: UTF-8 string: user 1 response http_version: 1.1 recorded_at: Tue, 01 Nov 2011 04:58:44 GMT - request: method: post uri: http://example.net/some/long/path body: encoding: UTF-8 string: headers: X-User-Id: - 2 response: status: code: 200 message: OK headers: Content-Length: - 15 body: encoding: UTF-8 string: user 2 response http_version: 1.1 recorded_at: Tue, 01 Nov 2011 04:58:44 GMT recorded_with: VCR 2.0.0注意 cassette 中请求头的 YAML 表示形式X-User-Id的值是列表- 1。这是因为 VCR 在内部把每个头都规范化为字符串数组详见下文头值统一为数组一节cassette 持久化时也就以数组形式保存。第二步编写调用脚本创建header_matching.rb在use_cassette中通过match_requests_on: [:headers]指定使用请求头匹配include_http_adapter_for(http_lib) require vcr VCR.configure do |c| configuration c.cassette_library_dir cassettes end VCR.use_cassette(example, match_requests_on: [:headers]) do puts Response for user 2: response_body_for(:get, http://example.com/, nil, X-User-Id 2) end VCR.use_cassette(example, match_requests_on: [:headers]) do puts Response for user 1: response_body_for(:get, http://example.com/, nil, X-User-Id 1) end脚本中configuration与http_lib是占位符需要按实际使用的 HTTP 库替换为下表组合configurationhttp_libc.hook_into :webmockcurbc.hook_into :webmockpatronc.hook_into :webmockem-http-request即用 WebMock 作为拦截层配置项说明见 docs/configuration/hook_into.md并分别配合 curb、patron 或 em-http-request 三种 HTTP 客户端运行。第三步运行与预期输出执行ruby header_matching.rb两条请求虽然 URI 相同但因为请求头X-User-Id不同分别命中了 cassette 中对应的交互输出为Response for user 2: user 2 response Response for user 1: user 1 response这正是:headers匹配器的价值同一个端点、不同的用户身份各回各的响应。如果去掉match_requests_on: [:headers]即退回到默认的:method:uri匹配两条请求都会命中 cassette 中的第一条交互输出会变成两条 user 1 response。底层原理headers 是如何被比较的匹配执行链路当请求发生时cassette 会遍历内部维护的交互列表寻找与当前请求匹配的交互。核心逻辑位于 lib/vcr/cassette/http_interaction_list.rbdef interaction_matches_request?(request, interaction) request_matchers.all? do |matcher_name| matcher VCR.request_matchers[matcher_name] matcher.matches?(request, interaction.request) end end要点有二match_requests_on中的每个匹配器都必须通过all?语义。因此match_requests_on: [:headers]意味着请求头完全一致才会回放若写match_requests_on: [:method, :uri, :headers]则方法、URI、请求头三者全部一致才命中。匹配器通过VCR.request_matchers[matcher_name]从 请求匹配器注册表 中取出。注册表不仅内置了:method、:uri、:body、:headers、:host、:path、:query、:body_as_json等匹配器还支持通过c.register_request_matcher注册自定义匹配器详见 docs/request_matching/custom_matcher.md。头键大小写不敏感HTTP 头的名称在语义上是不区分大小写的X-User-Id与x-user-id等价但 Ruby 的 Hash 相等比较是区分大小写的。VCR 是如何处理的关键在 lib/vcr/structs.rb 的Normalizers::Header#normalize_headers。当Request或Response从 hash 构建时会执行头规范化def normalize_headers new_headers {} normalized_header_keys Hash.new {|h,k| k } headers.each do |k, v| val_array case v when Array then v when nil then [] else [v] end new_headers[String.new(k)] convert_to_raw_strings(val_array) normalized_header_keys[k.downcase] k end if headers self.headers new_headers end这里维护了一张normalized_header_keys映射键为小写形式值为原始键名用于后续的取头、改头、删头操作header_key/get_header/edit_header/delete_header见 lib/vcr/structs.rb。也就是说VCR 提供的get_header等辅助方法对大小写不敏感但:headers匹配器本身比较的是规范化后的原始哈希r1.headers r2.headers键名仍按原样保存。因此实践中请尽量保持录制与回放时请求头键名一致如统一大写首字母风格避免因纯大小写差异导致匹配失败。头值统一为数组从上面对比可以看到传入的单值头如字符串1会被包装为数组[1]nil值被规范化为空数组[]已经是数组的保持不变。随后 convert_to_raw_strings 会把数组中的字符串元素转为原始字符串String.new(v)避免某些 HTTP 库对字符串做子类化或附加实例变量后导致 YAML 序列化异常。因此在比较层面:headers匹配的是头名 → 字符串值数组的哈希相等。单元测试佐证spec/lib/vcr/request_matcher_registry_spec.rb 为:headers提供了两条规格相同请求头、不同插入顺序 → 匹配成功{ a 1, b 2 }与{ b 2, a 1 }判定为匹配Ruby 哈希相等与键顺序无关任一请求头值不同 → 匹配失败{ a 3, b 2 }与{ b 2, a 1 }判定为不匹配。这两条规格精确刻画了:headers的语义只看头集合是否完全相同头键的插入顺序不影响结果但任一头的值不同即失败。与默认匹配器及其他匹配器组合:headers通常不单独使用。cassette 的match_requests_on选项接受一个匹配器数组VCR 会要求所有匹配器同时通过all?组合方式如下VCR.use_cassette(example, match_requests_on: [:method, :uri, :headers]) do # 方法 URI 请求头 全部一致才会回放 end内置的其他匹配器均在 lib/vcr/request_matcher_registry.rb 中注册匹配器比较逻辑适用场景:methodr1.method r2.methodHTTP 方法一致:urir1.parsed_uri r2.parsed_uri完整 URI含 query一致:bodyr1.body r2.body请求体一致见 docs/request_matching/body.md:headersr1.headers r2.headers请求头完全一致本文主题:host解析后 URI 的 host 一致跨路径只按主机名匹配:path解析后 URI 的 path 一致忽略 query 只按路径匹配见 docs/request_matching/path.md:query经query_parser解析后的 query 一致忽略 query 键顺序见 docs/request_matching/query.md:body_as_json按 JSON 语义比较请求体JSON 键顺序无关见 docs/request_matching/body_as_json.md一个实用的组合是match_requests_on: [:method, :host, :path, :headers]既避免 query 中时间戳、签名等动态参数干扰匹配可进一步配合VCR.request_matchers.uri_without_params忽略指定 query 参数见 docs/request_matching/uri_without_param.md又能用请求头区分同一端点上的不同业务语义。实践注意事项cassette 中请求头必须是数组形式由于 VCR 内部将头值规范化为数组手写或生成的 cassette 中请求头应写作X-User-Id: [1]的列表形式与录制产物保持一致。默认匹配器不含 headersmatch_requests_on未显式指定时VCR 使用DEFAULT_MATCHERS [:method, :uri]lib/vcr/request_matcher_registry.rb。需要按请求头区分交互时必须在use_cassette或default_cassette_options中显式传入:headers。请求头键名大小写:headers的相等比较基于原始键名哈希建议录制与回放两端保持一致的键名风格如依赖 VCR 提供的get_header等辅助方法读取头则这些方法本身大小写不敏感。动态请求头要谨慎如果请求头中包含每次运行都会变化的随机 token如时间戳签名:headers会导致回放几乎永远无法命中。此时应优先使用before_record/before_playback钩子见 docs/hooks/before_record.md 与 docs/hooks/before_playback.md将动态头值过滤或替换为固定值再启用:headers匹配。匹配失败的表现如果当前请求无法命中 cassette 中的任何交互VCR 会按 record mode 决定是发起真实请求并补录:all/:new_episodes见 docs/record_modes/all.md还是抛出未处理请求错误请结合记录模式文档排查。参考链接本文主题文档docs/request_matching/headers.mdCucumber 特性规格features/request_matching/headers.feature匹配器注册与实现lib/vcr/request_matcher_registry.rb头规范化实现lib/vcr/structs.rb交互匹配执行链路lib/vcr/cassette/http_interaction_list.rb匹配器单元测试spec/lib/vcr/request_matcher_registry_spec.rb赞分享测试开发工具【免费下载链接】vcrRecord your test suites HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.项目地址https://gitcode.com/gh_mirrors/vc/vcr点击查看免费下载相关推荐VCR 请求匹配之 :uri 匹配器按请求 URI 精确回放录制的 HTTP 交互VCR 请求匹配之 :uri 匹配器按请求 URI 精确回放录制的 HTTP 交互 本篇指南讲解 VCRVideo Cassette Recorder请求测试开发工具VCR 请求匹配器 :body基于请求体精确匹配 HTTP 交互的完整实战指南VCR 请求匹配器 :body 基于请求体精确匹配 HTTP 交互的完整实战指南 在 VCR 中 match_requests_on 决定了录制好的 cas测试开发工具VCR 请求匹配指南使用 :path 匹配器按 URI 路径回放 HTTP 交互VCR 请求匹配指南使用 :path 匹配器按 URI 路径回放 HTTP 交互 本文聚焦 VCR 内置请求匹配器 :path 它只比较请求 URI 的 p测试开发工具上一篇Umi Plugin Qiankun 安装与配置指南下一篇Galacticraft终极指南从零开始搭建你的星际探索之旅 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考