首页 > 解决方案 > 如何使用 minitest 在 Ruby on Rails 6 中测试文件上传?

问题描述

我有这样的控制器方法:

# POST /v1/scans
  def create
    p params
    uploaded_file = params["pm_file"]
    filename      = uploaded_file.original_filename
    content       = uploaded_file.read
    parser = ParserStrategy.parser_for filename
    file_scan = parser.parse_content content, filename, @api_token.organisation

    data = {'scan.id': file_scan.id.to_s}
    render json: data, status: :created 
  rescue => e
    Rails.logger.error e.message
    Rails.logger.error e.backtrace.join("\n")
  end

文件上传工作正常。如果我像这样用 CURL 测试它:

curl -H "Authorization: Bearer QGoEUTK5htI6IT1zyLgKWPqBxv1EXrf7ApSFuuSi" -X POST http://localhost:3000/v1/scans -F pm_file=@composer.json

我得到了预期的结果,参数输出如下所示:

Parameters: {"pm_file"=>#<ActionDispatch::Http::UploadedFile:0x00007f914bae9708 @tempfile=#<Tempfile:/var/folders/ng/h_lxfcf13m71_kgr70_f79hr0000gn/T/RackMultipart20200521-75565-r5a4k1.json>, @original_filename="composer.json", @content_type="application/octet-stream", @headers="Content-Disposition: form-data; name=\"pm_file\"; filename=\"composer.json\"\r\nContent-Type: application/octet-stream\r\n">}
<ActionController::Parameters {"pm_file"=>#<ActionDispatch::Http::UploadedFile:0x00007f914bae9708 @tempfile=#<Tempfile:/var/folders/ng/h_lxfcf13m71_kgr70_f79hr0000gn/T/RackMultipart20200521-75565-r5a4k1.json>, @original_filename="composer.json", @content_type="application/octet-stream", @headers="Content-Disposition: form-data; name=\"pm_file\"; filename=\"composer.json\"\r\nContent-Type: application/octet-stream\r\n">, "controller"=>"v1/scans", "action"=>"create"} permitted: false>

我可以清楚地看到这pm_file是一个 UploadedFile 类型的对象。现在我想为它写一个自动化测试。用 minitest 这样做:

test "should return 201 because valid request" do
  file_path = "#{Rails.root}/test/fixtures/files/composer/composer.json"
  composer_file = Rack::Test::UploadedFile.new(file_path, 'application/json', false)
  post v1_scans_url, params: { pm_file: composer_file }, headers: { "Authorization" => "Bearer #{@token}" }, as: :json
  assert_response 201
end

不幸的是,这不起作用。然后我启动测试,参数输出如下所示:

Parameters: {"pm_file"=>{"original_filename"=>"composer.json", "tempfile"=>"#<File:0x00007fd2a11dff30>", "content_type"=>"application/json"}, "scan"=>{"pm_file"=>{"original_filename"=>"composer.json", "tempfile"=>"#<File:0x00007fd2a11dff30>", "content_type"=>"application/json"}}}

现在pm_file,它不再是 UploadedFile 类型的 Object,而是一个 Hash。并且代码失败了:

undefined method `original_filename' for #<ActionController::Parameters:0x00007fd2859c2278>

同样的情况也发生在fixture_file_upload. 这个测试带来了完全相同的结果:

test "should return 201 because valid request" do
  file_path = "#{Rails.root}/test/fixtures/files/composer/composer.json"
  composer_file = fixture_file_upload("#{Rails.root}/test/fixtures/files/composer/composer.json",'application/json')
  post v1_scans_url, params: { pm_file: composer_file }, headers: { "Authorization" => "Bearer #{@token}" }, as: :json
  assert_response 201
end

我做错了什么?如何正确测试 Rails 6 中的文件上传?

标签: ruby-on-railsrubyminitest

解决方案


问题是我试图通过 JSON 发送文件。删除as: :json帖子末尾的代码段后,它按预期工作。所以这里的这个测试就像使用 Minitest 上传文件的预期一样工作:

test "should return 201 because valid request" do
  file_path = "#{Rails.root}/test/fixtures/files/composer/composer.json"
  composer_file = fixture_file_upload("#{Rails.root}/test/fixtures/files/composer/composer.json",'application/json')
  post v1_scans_url, params: { pm_file: composer_file }, headers: { "Authorization" => "Bearer #{@token}" }
  assert_response 201
end

推荐阅读