You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails中Minitest+Faraday测试WebMock存根匹配失败报错

问题分析与解决方法

你的问题核心是WebMock存根无法匹配真实请求,导致触发了真实网络请求(从而抛出WebMock::NetConnectNotAllowedError)。虽然你打印的请求体看起来和存根一致,但报错显示Content-Length为0,说明请求实际发送的body是空的,或者存根的匹配条件存在隐形不匹配。

可能原因与对应解决方案

1. 请求头缺少Content-Type: application/json

Faraday发送JSON格式的POST请求时,必须设置正确的Content-Type头,否则服务器(包括WebMock)可能无法识别请求体,导致body被忽略。

  • 修改服务对象的header设置:
    在MovidaApi的初始化方法中添加Content-Type头:

    class MovidaApi
      def initialize(payload)
        @payload = payload
        @headers = {
          'Content-Type' => 'application/json',
          # 其他你需要的请求头
        }
      end
    
      # ...其他方法
    end
    
  • 同步修改WebMock存根的匹配条件:
    存根需要同时匹配body和请求头,确保WebMock能识别到正确的请求:

    test "create a title" do
      body = { 
        "name" => 'some name',
        "external_id" =>'004',
        "title_type" =>'feature',
        "tags" => 'some tag' 
      }.to_json
    
      stub_request(:post, "some web")
        .with(
          body: body,
          headers: { 'Content-Type' => 'application/json' }
        )
        .to_return(status: 201, body: "Created", headers: {})
    
      response = MovidaApi.new(payload).create_title
    
      assert response.success?
      assert_equal "Created", response.body
    end
    

2. 使用WebMock的json:参数替代body:参数

手动转JSON字符串容易出现隐形差异(比如空格、换行符、引号格式),WebMock提供了json:参数,可以直接匹配哈希,自动处理JSON编码的匹配逻辑,避免字符串匹配的问题:

test "create a title" do
  expected_body = { 
    "name" => 'some name',
    "external_id" =>'004',
    "title_type" =>'feature',
    "tags" => 'some tag' 
  }

  stub_request(:post, "some web")
    .with(json: expected_body)
    .to_return(status: 201, body: "Created", headers: {})

  response = MovidaApi.new(payload).create_title

  assert response.success?
  assert_equal "Created", response.body
end

3. 检查Faraday中间件是否冲突

如果你的Faraday连接配置了Faraday::Request::Json中间件,它会自动将哈希转成JSON并设置Content-Type。但你现在是手动将哈希转成JSON字符串后赋值给conn.body,这会导致中间件重复编码,最终请求体变成"\"{...}\""(字符串嵌套),从而无法匹配存根。

  • 解决方法:
    如果手动处理JSON字符串,就移除Faraday::Request::Json中间件;如果保留该中间件,就直接传递哈希给conn.body,不需要手动转JSON:
    # 服务对象中的api_call_post方法修改为:
    def api_call_post(connection, message, url)
      pp message
      response = connection.post do |conn|
        conn.url url
        conn.body = message # message直接传哈希,由中间件转JSON
        conn.headers = @headers
      end
      check_response(response)
    end
    

内容的提问来源于stack exchange,提问作者Jose Joaquin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:16:26