testar um upload de arquivo usando rspec - rails

142

Quero testar um upload de arquivo em trilhos, mas não sei como fazer isso.

Aqui está o código do controlador:

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

Eu tentei essa especificação, mas ela não funciona:

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

Como posso simular o upload de arquivos usando o rspec?

user727403
fonte

Respostas:

291

Você pode usar fixture_file_upload método para upload de arquivos de teste: Coloque o seu arquivo de teste no "{Rails.root} / spec / luminárias / arquivos" diretório

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

Caso você estivesse esperando o arquivo na forma de params ['upload'] ['datafile']

it "can upload a license" do
  file = Hash.new
  file['datafile'] = @file
  post :uploadLicense, :upload => file
  response.should be_success
end
ebsbk
fonte
8
Isso deve ser marcado como a resposta, porque está correto. Valeu cara!
Emil Ahlbäck
30
Consulte bit.ly/OSrL7R (questão de estouro de pilha 3966263) se você estiver obtendo um arquivo que não possui erros. Um formulário diferente é necessário no Rails 3.2: @file = Rack :: Test :: UploadedFile.new (Rails.root.join ('spec / fixtures / files / test.csv'), 'text / csv')
Mike Blyth
3
fixture_file_upload também funciona se você especificar o caminho completo para o arquivo: "Rails.root.join ( 'spec / luminárias / files / test.csv"
jmanrubia
1
fixture_file_upload está sendo tratado como uma string nos parâmetros, alguém sabe por quê?
Abe Petrillo
3
@AbePetrillo (ou quem vê o comentário e tem a mesma pergunta) Eu tive o mesmo problema. No meu caso, o primeiro argumento para postfoi um método auxiliar de caminho, cujo único argumento pretendido não coloquei entre parênteses, portanto, os seguintes tokens foram interpretados como argumentos adicionais para o auxiliar, em vez de argumentos para a própria publicação. Por exemplo, eu tinha em post order_documents_path @order, document: filevez de post order_documents_path(@order), document: file.
soupdog
54

Não tenho certeza se você pode testar o upload de arquivos usando apenas o RSpec. Você já experimentou a Capivara ?

É fácil testar o upload de arquivos usando o attach_filemétodo da capivara a partir de uma especificação de solicitação.

Por exemplo (este código é apenas uma demonstração):

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
fonte
6
Claro que isso funciona em uma especificação de integração. A pergunta do OP diz respeito a uma especificação da unidade controladora, considerando que ele está postando apenas o código do controlador. Só estou dizendo se alguém está confuso. Faça isso em uma especificação de recurso, faça a resposta do ebsbk em uma especificação de unidade.
Starkers
2
As necessidades caminho do arquivo para estar entre aspas
sixty4bit
32

se você incluir Rack :: Test *, inclua simplesmente os métodos de teste

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

então você pode usar o método UploadedFile:

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

* NOTA: Meu exemplo é baseado no Sinatra, que estende o Rack, mas deve funcionar com o Rails, que também usa Rack, TTBOMK

zedd45
fonte
3
FYI: Você não precisa necessariamente include Rack::Test::Methodsusar o Rack :: Test :: UploadedFile. require 'rack/testbasta.
procurando
3
Eu nem preciso require 'rack/test'. se você estiver usando, Rack::Test::UploadedFileé bom o suficiente para usá-lo. Desde que a configuração do seu aplicativo Rails esteja correta. PS: Estou no Rails 4eruby 2.1
Vishnu Narang
O comentário de Vishnu é o mais preciso, pois ele exige o método explicitamente. A inclusão rack/testincluirá tudo, desde teste, o que inclui test/methods, mas também inclui tudo em teste, portanto, provavelmente, mais do que você precisa.
zedd45
18

Não fiz isso usando o RSpec, mas tenho um teste Test :: Unit que faz algo semelhante ao fazer upload de uma foto. Configurei o arquivo carregado como uma instância de ActionDispatch :: Http :: UploadedFile, da seguinte maneira:

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

Algo semelhante pode funcionar para você também.

Dave Isaacs
fonte
6

Foi assim que eu fiz Rails 6, RSpeceRack::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

NÃO incluaActionDispatch::TestProcess ou qualquer outro código, a menos que tenha certeza do que está incluindo.

thisismydesign
fonte
4

Eu tive que adicionar os dois inclui para fazê-lo funcionar:

describe "my test set" do
  include Rack::Test::Methods
  include ActionDispatch::TestProcess
nfriend21
fonte
1
Nunca, jamais, incluem ActionDispatch :: TestProcess explicação: github.com/honeybadger-io/honeybadger-ruby/blob/...
gotar