Rails 5 RSpec上传文件测试失败:Stub参数类型不匹配求助
你遇到的问题本质很清晰:测试里用fixture_file_upload生成的是Rack::Test::UploadedFile实例,但实际控制器接收的是ActionDispatch::Http::UploadedFile,导致RSpec的with参数匹配失败。下面给你几个可行的修复方案:
方案一:直接创建ActionDispatch::Http::UploadedFile实例
把原来用fixture_file_upload生成文件的代码,改成手动创建控制器实际接收的文件类型实例:
let(:csv_statements) do [ ActionDispatch::Http::UploadedFile.new( tempfile: File.open(Rails.root.join('spec', 'fixtures', 'transaction.csv')), original_filename: 'transaction.csv', content_type: 'application/csv' ) ] end
这样你的stub参数类型就和控制器实际传入的完全一致,with匹配就能成功。
方案二:用属性匹配器忽略类型差异
如果不需要严格匹配实例类型,只关心文件的关键属性(比如文件名、内容类型),可以用RSpec的属性匹配器来灵活匹配:
allow(ABCCLass::XYZProcessor).to receive(:prepare_processing) .with( array_including( have_attributes( original_filename: 'transaction.csv', content_type: 'application/csv' ) ) ) .and_return(nil)
这种方式不管是Rack::Test::UploadedFile还是ActionDispatch::Http::UploadedFile,只要属性符合就会匹配,适用性更广。
方案三:先设置默认Stub(针对报错提示)
你的报错里提到Please stub a default value first if message might be received with other args as well,如果这个方法可能被其他参数调用,可以先设置一个默认的stub,再针对特定参数做匹配:
# 先给所有调用设置默认返回 allow(ABCCLass::XYZProcessor).to receive(:prepare_processing).and_return(nil) # 再针对你的目标参数设置匹配(结合方案一或二修复类型问题) allow(ABCCLass::XYZProcessor).to receive(:prepare_processing) .with(csv_statements) .and_return(nil)
这个方案能避免因意外参数调用导致的报错,同时结合前面的类型修复就能彻底解决问题。
补充:关于报错回溯的说明
从你提供的回溯信息能明确看到两边的实例类型差异:
-[[#<Rack::Test::UploadedFile:...>]] +[[#<ActionDispatch::Http::UploadedFile:...>]]
所以只要让stub的参数和实际传入的类型/属性一致,就能解决这个匹配失败的问题。
内容的提问来源于stack exchange,提问作者Shehbaz

