Testen Sie einen Datei-Upload mit rspec - Rails

142

Ich möchte einen Datei-Upload in Rails testen, bin mir aber nicht sicher, wie ich das machen soll.

Hier ist der Controller-Code:

def uploadLicense
    #Create the license object
    @license = License.create(params[:license]) 


    #Get Session ID
    sessid = session[:session_id]

    puts "\n\nSession_id:\n#{sessid}\n"

    #Generate a random string
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
    newpass = ""
    1.upto(5) { |i| newpass << chars[rand(chars.size-1)] }

    #Get the original file name
    upload=params[:upload]
    name =  upload['datafile'].original_filename 

    @license.format = File.extname(name)

    #calculate license ID and location
    @license.location = './public/licenses/' + sessid + newpass + name 

    #Save the license file
    #Fileupload.save(params[:upload], @license.location) 
    File.open(@license.location, "wb") { |f| f.write(upload['datafile'].read) }

     #Set license ID
    @license.license_id = sessid + newpass

    #Save the license
    @license.save

    redirect_to :action => 'show', :id => @license.id 
end

Ich habe diese Spezifikation ausprobiert, aber sie funktioniert nicht:

it "can upload a license and download a license" do
    file = File.new(Rails.root + 'app/controllers/lic.xml')
    license = HashWithIndifferentAccess.new
    license[:datafile] = file
    info = {:id => 4}
    post :uploadLicense, {:license => info, :upload => license}
end

Wie kann ich den Datei-Upload mit rspec simulieren?

user727403
quelle

Antworten:

291

Sie können mit fixture_file_upload Methode zur Testdatei Upload: Legen Sie Ihre Testdatei in „{Rails.root} / spec / Einrichtung / Dateien“ Verzeichnis

before :each do
  @file = fixture_file_upload('files/test_lic.xml', 'text/xml')
end

it "can upload a license" do
  post :uploadLicense, :upload => @file
  response.should be_success
end

Falls Sie die Datei in Form von Parametern erwartet haben ['upload'] ['datafile']

it "can upload a license" do
  file = Hash.new
  file['datafile'] = @file
  post :uploadLicense, :upload => file
  response.should be_success
end
ebsbk
quelle
8
Dies sollte als Antwort markiert werden, da es korrekt ist. Danke, Mann!
Emil Ahlbäck
30
Siehe bit.ly/OSrL7R (Stapelüberlauffrage 3966263), wenn Sie keine Fehler in der Datei erhalten. In Rails 3.2 wird ein anderes Formular benötigt: @file = Rack :: Test :: UploadedFile.new (Rails.root.join ('spec / fixtures / files / test.csv'), 'text / csv')
Mike Blyth
3
fixture_file_upload funktioniert auch, wenn Sie den vollständigen Pfad zur Datei angeben: "Rails.root.join ('spec / fixtures / files / test.csv"
jmanrubia
1
fixture_file_upload wird in params als String behandelt. Weiß jemand warum?
Abe Petrillo
3
@AbePetrillo (oder wer auch immer den Kommentar sieht und die gleiche Frage hat) Ich hatte das gleiche Problem. In meinem Fall war das erste Argument posteine Pfadhilfemethode, deren einziges beabsichtigtes Argument ich nicht in Klammern gesetzt habe. Daher wurden die folgenden Token als zusätzliche Argumente für den Helfer und nicht als Argumente für den Beitrag selbst interpretiert. ZB hatte ich post order_documents_path @order, document: filestatt post order_documents_path(@order), document: file.
Suppenhund
54

Ich bin nicht sicher, ob Sie Datei-Uploads nur mit RSpec testen können. Haben Sie Capybara ausprobiert ?

Es ist einfach, Datei-Uploads mit der attach_fileMethode von capybara aus einer Anforderungsspezifikation zu testen .

Zum Beispiel (dieser Code ist nur eine Demo):

it "can upload a license" do
  visit upload_license_path
  attach_file "uploadLicense", /path/to/file/to/upload
  click_button "Upload License"
end

it "can download an uploaded license" do
  visit license_path
  click_link "Download Uploaded License"
  page.should have_content("Uploaded License")
end
Ken
quelle
6
Dies funktioniert natürlich in einer Integrationsspezifikation. Die Frage von OP betrifft eine Spezifikation der Controller-Einheit, da er nur Controller-Code veröffentlicht. Ich sage nur, wenn jemand verwirrt ist. Tun Sie dies in einer Feature-Spezifikation, machen Sie die Antwort von ebsbk in einer Einheitenspezifikation.
Starkers
2
Der Dateipfad muss in Anführungszeichen stehen
sixty4bit
32

Wenn Sie Rack :: Test * einschließen, geben Sie einfach die Testmethoden an

describe "my test set" do
  include Rack::Test::Methods

Dann können Sie die UploadedFile-Methode verwenden:

post "/upload/", "file" => Rack::Test::UploadedFile.new("path/to/file.ext", "mime/type")

* HINWEIS: Mein Beispiel basiert auf Sinatra, das Rack erweitert, aber mit Rails funktionieren sollte, das auch Rack, TTBOMK verwendet

zedd45
quelle
3
Zu Ihrer Information: Sie müssen nicht unbedingt include Rack::Test::MethodsRack :: Test :: UploadedFile verwenden. require 'rack/testreicht.
Xentek
3
Ich muss nicht einmal require 'rack/test'. Wenn Sie verwenden Rack::Test::UploadedFile, ist das gut genug, um es zu verwenden. Vorausgesetzt, das Setup Ihrer Rails-App ist in Ordnung. PS: Ich bin auf Rails 4undruby 2.1
Vishnu Narang
Vishnus Kommentar ist der genaueste, da er die Methode explizit benötigt. Das Einschließen rack/testumfasst alles aus dem Test, einschließlich test/methods, aber auch alles im Test, also wahrscheinlich mehr als Sie benötigen.
Zedd45
18

Ich habe dies nicht mit RSpec gemacht, aber ich habe einen Test :: Unit-Test, der etwas Ähnliches zum Hochladen eines Fotos macht. Ich habe die hochgeladene Datei wie folgt als Instanz von ActionDispatch :: Http :: UploadedFile eingerichtet:

test "should create photo" do
  setup_file_upload
  assert_difference('Photo.count') do
    post :create, :photo => @photo.attributes
  end
  assert_redirected_to photo_path(assigns(:photo))
end


def setup_file_upload
  test_photo = ActionDispatch::Http::UploadedFile.new({
    :filename => 'test_photo_1.jpg',
    :type => 'image/jpeg',
    :tempfile => File.new("#{Rails.root}/test/fixtures/files/test_photo_1.jpg")
  })
  @photo = Photo.new(
    :title => 'Uploaded photo', 
    :description => 'Uploaded photo description', 
    :filename => test_photo, 
    :public => true)
end

Ähnliches könnte auch für Sie funktionieren.

Dave Isaacs
quelle
6

So habe ich es gemacht Rails 6, RSpecundRack::Test::UploadedFile

describe 'POST /create' do
  it 'responds with success' do
    post :create, params: {
      license: {
        picture: Rack::Test::UploadedFile.new("#{Rails.root}/spec/fixtures/test-pic.png"),
        name: 'test'
      }
    }

    expect(response).to be_successful
  end
end

Fügen Sie KEINENActionDispatch::TestProcess oder anderen Code hinzu, es sei denn, Sie sind sich sicher, was Sie einschließen.

Das ist mein Design
quelle
4

Ich musste beide Includes hinzufügen, damit es funktioniert:

describe "my test set" do
  include Rack::Test::Methods
  include ActionDispatch::TestProcess
nfriend21
quelle
1
Fügen Sie niemals eine Erklärung zu ActionDispatch :: TestProcess hinzu: github.com/honeybadger-io/honeybadger-ruby/blob/…
gotar