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
相关产品推荐
相关产品推荐

