简体   繁体   English

如何在 Rails Rspec 请求规范中上传文件

[英]How to upload a file in a Rails Rspec request spec

I want to test uploading a file in a Rails Rspec request test.我想在 Rails Rspec 请求测试中测试上传文件。 I'm using Paperclip for asset storage.我正在使用 Paperclip 进行资产存储。

I've tried:我试过了:

path = 'path/to/fixture_file'
params = { file: Rack::Test::UploadedFile.new(path, 'application/pdf', true) }
post v1_product_documents_path, params: params

In the controller, I get a string在 controller 中,我得到一个字符串

"#Rack::Test::UploadedFile:0x0055b544479128>" “#Rack::Test::UploadedFile:0x0055b544479128>”

instead of the actual file.而不是实际的文件。

The same code works in controller tests相同的代码在 controller 测试中有效

try to use fixture_file_upload : fixture_file_upload尝试使用fixture_file_uploadfixture_file_upload

or if you wanted to use this in a factory或者如果您想在工厂中使用它

 Rack::Test::UploadedFile.new(File.open(File.join(Rails.root, '/spec/fixtures/images/bob-weir.jpg')))

In rails_helper.rb do在 rails_helper.rb 做

include ActionDispatch::TestProcess
include Rack::Test::Methods

Then you have few options.那么你有几个选择。 1. You can use fixture_file_upload helper 1.你可以使用fixture_file_upload helper

  let(:file) { fixture_file_upload('files/image.jpg') }
  it 'it should work' do
    post some_path, params: { uploads: { file: file } }
  end
  1. You can use Uploaded file but you have to give full path您可以使用上传的文件,但必须提供完整路径
  let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec',
  'fixtures', 'blank.jpg'), 'image/jpg') }
  let(:params) { { attachment: file, variant_ids: ['', product.master.id] } }
  let(:action) { post :create, product_id: product.slug, image: params }

Under describe block, include these modulesdescribe块下,包括这些模块

include Rack::Test::Methods
include ActionDispatch::TestProcess

Now try running the specs现在尝试运行规范

path = 'path/to/fixture_file'
params = { "file" => Rack::Test::UploadedFile.new(path, 'application/pdf', true) }
post v1_product_documents_path, params: params

Also, I guess you forgot to add , between v1_product_documents_path and params: params , please add that and let me know.另外,我想你忘了加,之间v1_product_documents_pathparams: params ,请补充说明,让我知道。

Hope that helps!希望有帮助!

For rails 6 no need to do includes, just fixture_file_upload .对于 rails 6 不需要做包含,只需要fixture_file_upload If you are using spec/fixtures/files folder for fixtures you can use file_fixture helper如果您使用spec/fixtures/files夹作为装置,您可以使用file_fixture helper

let(:csv_file) { fixture_file_upload(file_fixture('file_example.csv')) }

subject(:http_request) { post upload_file_path, params: { file: csv_file } }

可能对其他用户有用:我遇到这个问题是因为我在规范中错误地使用了get而不是post请求。

对于其他人在实施上述批准的解决方案后"#Rack::Test::UploadedFile:0x0055b544479128>"在控制器中获得类似"#Rack::Test::UploadedFile:0x0055b544479128>"的信息,我通过将content-type标题更改为'application/x-www-form-urlencoded'来解决此问题'application/x-www-form-urlencoded'以便接受表单数据。

 file = File.expand_path("path/to/fixture_file", __dir__)
 Rack::Test::UploadedFile.new(file, 'application/pdf')

If you need the content type in your RSpec test, it seems you can pass it as an argument to fixture_file_upload (no idea why this method does not derive the content type from the OS).如果您在 RSpec 测试中需要content type ,您似乎可以将其作为参数传递给fixture_file_upload (不知道为什么此方法不从操作系统派生内容类型)。

For instance:例如:

fixture_file_upload("pdfs/some.pdf", "application/pdf")

I ran into this issue while writing request specs for a POST endpoint that processes incoming email payloads that include file attachments.我在为处理包含文件附件的传入 email 有效载荷的 POST 端点编写请求规范时遇到了这个问题。

this is what the request looked like in the request spec:这是请求规范中请求的样子:

post api_v1_email_processor_path, params: params, as: :json

where params[:attachment] was:其中params[:attachment]是:

fixture_file_upload("spec/support/fixtures/files/report.csv", 'text/csv',)

and when I added some breakpoints in the controller, this is what the attachment was being converted into:当我在 controller 中添加一些断点时,这就是附件被转换成的内容:

{"original_filename"=>"report.csv", "tempfile"=>"#<File:0x0000000121d2bfc8>", "content_type"=>"text/csv"}

as you can see, the "tempfile" was being cast to a string value of "<File:0x0000000121d2bfc8>"如您所见, "tempfile"被转换为字符串值"<File:0x0000000121d2bfc8>"

when I emulated the same request in a controller spec, to my huge confusion, there was no casting, and the breakpoint showed the param (correctly) as:当我在controller规范中模拟相同的请求时,令我非常困惑的是,没有强制转换,断点将参数(正确地)显示为:

#<ActionDispatch::Http::UploadedFile:0x000000012d559488 @tempfile=#<Tempfile:/var/folders/jg/n_0rknb97kvddb0jgh6r3_x40000gn/T/RackMultipart20221008-45122-mzyqg5.csv>, @original_filename="report.csv", @content_type="text/csv", @headers="Content-Disposition: form-data; name=\"attachment1\"; filename=\"report.csv\"\r\nContent-Type: text/csv\r\nContent-Length: 24931\r\n">]

after seeing L.Youl's answer https://stackoverflow.com/a/67987482/14926068 , it got me thinking that the issue was with how the request payload was being formatted, prior to being sent to the server through the Rack middleware.在看到 L.Youl 的回答https://stackoverflow.com/a/67987482/14926068后,我开始思考问题出在请求有效负载在通过 Rack 中间件发送到服务器之前如何格式化。 and so I tried taking off the as: :json :所以我试着取消as: :json :

post api_v1_email_processor_path, params: params

and bingo, I got the same result as with the controller spec.和宾果游戏,我得到了与 controller 规范相同的结果。

after inspecting response.request.content_type in my request spec, I found that when I took the as: :json off, its value changed from "application/json" to在我的请求规范中检查response.request.content_type后,我发现当我关闭as: :json时,它的值从"application/json"变为

"multipart/form-data; boundary=----------XnJLe9ZIbbGUYtzPQJ16u1"

for completeness, I checked response.request.content_type in the controller spec as well, and found that even with as: :json in the request, the content_type was being set to "multipart/form-data" , and that accounted for the discrepancy in behavior between controller and request specs - it seems that as: :json only affects the "content-type" in the request specs.为了完整起见,我还检查了 controller 规范中的response.request.content_type ,发现即使在请求中使用as: :jsoncontent_type也被设置为"multipart/form-data" ,这就是差异的原因在 controller 和请求规范之间的行为中 - 似乎as: :json只影响请求规范中的"content-type"

in retrospect, it was obvious - json cannot be used to send actual files.回想起来,很明显 - json 不能用于发送实际文件。 but it took me a long time to figure it out because the equivalent controller spec was working fine and this was literally the only endpoint in my Rails api-only app that expected non-json payloads但我花了很长时间才弄明白,因为等效的 controller 规范工作正常,这实际上是我的 Rails api-only 应用程序中唯一预期非 json 有效载荷的端点

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM