RSpec请求测试ActiveStorage与FactoryBot报错:签名不匹配
解决RSpec请求测试中ActiveStorage+FactoryBot的签名错误问题
这个错误的核心原因是:你直接把FactoryBot工厂里定义的Rack::Test::UploadedFile对象当作参数传给PATCH请求时,Rack::Test会把这个对象序列化为字符串(就是日志里看到的#<Rack::Test::UploadedFile:0x0000000122758d98>),而非正确识别为上传文件,导致后端收到的不是ActionDispatch::Http::UploadedFile实例,触发ActiveStorage的签名验证失败。
下面是具体的解决方法:
方法一:测试用例中手动构造上传参数(最直接有效)
不要从FactoryBot生成的用户实例里取picture属性,而是在测试代码里单独创建Rack::Test::UploadedFile实例,直接传入请求参数:
# 测试文件内 let(:test_image) { Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'files', 'avatar.jpg'), 'image/jpeg') } it "成功更新用户头像" do patch user_path(target_user), params: { user: { picture: test_image } } # 这里写你的断言逻辑,比如检查头像是否成功附加 end
这样Rack::Test会自动识别这是上传文件,将其转为ActionDispatch::Http::UploadedFile传给后端,匹配浏览器上传的格式。
方法二:调整FactoryBot工厂的用途
如果想复用上传文件的逻辑,不要在工厂里直接定义picture属性(因为ActiveStorage的附件不是普通模型属性),而是用trait来生成已附加图片的用户实例,供测试中作为初始状态使用:
# factories/users.rb FactoryBot.define do factory :user do name { "测试用户" } # 其他基础属性... trait :with_picture do after(:build) do |user| user.picture.attach( io: File.open(Rails.root.join('spec', 'fixtures', 'files', 'avatar.jpg')), filename: 'avatar.jpg', content_type: 'image/jpeg' ) end end end end
测试更新场景时,还是用方法一的方式手动构造上传参数,这个trait仅用来生成已有头像的用户,方便测试更新前后的状态对比。
额外注意事项
- 确保测试用的文件路径正确,比如
spec/fixtures/files下确实存在对应的测试图片,避免文件找不到的问题。 - 不需要手动设置
multipart/form-data请求头,只要参数里有Rack::Test::UploadedFile实例,Rack::Test会自动处理请求格式。
内容的提问来源于stack exchange,提问作者user502052
相关产品推荐
相关产品推荐

