Skip to content
janeiro 23, 2009 / cassiomarques

Como testar upload de arquivos com o Cucumber

Em um artigo anterior eu mostrei como utilizar o Cucumber para criar testes funcionais para sua aplicação web. Hoje vou mostrar como utilizar o Cucumber para testar uploads de arquivos. Vou usar como exemplo uma aplicação Rails, mas de forma geral as técnicas descritas aqui poderiam ser utilizadas para testar upload de arquivos em qualquer aplicação web.

Vamos considerar um controller ProductsController com uma action create-from_csv_file cujo propósito é receber um arquivo texto com valores separados por vírgulas e gravar os dados de cada linha como um novo objeto da classe Product persistido no banco de dados. Qual seria o comportamento esperado para uma action como essa? Vamos descrever esse comportamento em uma feature escrita com o Cucumber:

  
  Feature: CSV file upload
    In order to create new products in the database
    As a user
    I want to be able to upload a CSV file with the products' data

    Scenario: Uploading a valid file with data for 3 new products
      Given I visit the file upload page
      When I upload a file with valid data for 3 new products
      Then the number of products should change to 3
      And I should be sent to the products index page

    Scenario: Uploading a malformed file
      Given I visit the file upload page
      When I upload a malformed file
      Then the number of products should not change
      And I should see the file upload page again
  
  

Temos dois cenários básicos listados, um para um arquivo correto e outro para um arquivo defeituoso. Ainda não temos código nenhum implementado para os passos que o Cucumber deve executar durantes os testes, logo essa é a próxima etapa. Vou mostrar apenas como poderiam ser escritos os passos que realizam o upload dos arquivos.

When /^I upload a file with valid data for 3 new products$/ do
  attach_file(:csv_file, File.join(RAILS_ROOT, 'features', 'upload-files', 'products_csv_ok.csv'))
  click_button "Send file"
end

When /^I upload a malformed file$/ do
  attach_file(:csv_file, File.join(RAILS_ROOT, 'features', 'upload-files', 'products_csv_bad.csv'))
  click_button "Send file"
end

A idéia aqui é criar dois arquivos, um correto e outro inválido, e colocá-los em algum diretório da sua aplicação. Eu coloquei dentro de features/upload-files, poderia ser colocado em qualquer outro lugar que você prefira. O método attach_file recebe um symbol com o nome do campo onde será informado o arquivo a enviar e o caminho para um arquivo em disco. Depois de associar o campo com o caminho para um arquivo, basta “clicar” no botão e fazer o submit do formulário.

Só por curiosidade, aqui está o código da action que escrevi:

def create_from_csv_file
  csv_data = params[:csv_file]
  if Product.create_from_csv(csv_data)
    flash[:notice] = "Products succesfully created"
    redirect_to products_url
  else
    flash[:error] = "Invalid file contents"
    render :action => "new_csv_file"
  end
end

Com essa solução você pode testar upload de qualquer tipo de arquivo. Um bom exemplo seria criar restrições para tipos de arquivos que podem ser recebidos, restrições para dimensões de imagens que podem ser enviadas, etc.

5 Comentários

Deixe um comentário
  1. Jonatas Paganini / jan 27 2009 4:46 pm

    Cassio, testei o seu exemplo e funcionou o teste do anexo perfeitamente
    mas nao consegui testar o redirecionamento:

    response.should redirect_to products_url

    Acredito que o seu teste nao envolva este tipo de teste, mas no meu passe sem este erro:

    expected redirect to {:controller=>”nome_meu_controller”}, got no redirect (Spec::Expectations::ExpectationNotMetError)

    E ele esta redirecionando, pois quando vejo o response.body funciona.

    []’s

  2. cassiomarques / jan 27 2009 4:59 pm

    @Jonatas,

    Dê uma olhada ai, pois mesmo que o response.body mostre o HTML da sua página, isso não significa que houve redirecionamento. Um render :action => “blabla” também vai exibir um HTML sem ter sido redirecionado. Use a velha técnica do que “passei por aqui” dentro da sua action ou então use o ruby-debug para ver se realmente está sendo redirecionado.

  3. Guilherme / jan 28 2009 11:43 pm

    Olá cassio, beleza?
    adorei seu artigo sobre cucumber, parabéns!
    como sou novo em bdd, você poderia me dizer como você fez para testar isto aqui?
    “Then the number of products should change to 3
    And I should be sent to the products index page”
    mesmo esquema do outro? guardar numa variavel de instancia o count antes de submeter o csv, e comparar com o count depois de submetido?
    Com uma função assim:
    Product.count.should == @actual_count + 3

    Você poderia usar também o matcher do RSPec : change(Product,:count).by(3) ?

    valeu abraço

  4. cassiomarques / jan 28 2009 11:56 pm

    @Guilherme,

    Para o “should change to …” eu sempre faço de uma duas formas a seguir: crio uma variável para guardar o valor atual (que vai virar “valor anterior”), executo a operação e depois vejo se esse valor + a variação esperada = valor atual. A outra forma é como você disse, usar o should change do RSpec, porém o problema disso é que você precisaria colocar a operação toda dentro de um lambda, como é feito com o as specs do Rspec

    lambda { Product.create( … ) }.should change(Product, :count).by(1)

    Com isso seria necessário implementar esse passo no Cucumber de forma que o submit do formulário ficasse dentro de um lambda por exemplo.

    Outro esquema é deixar que a user story dite os valores e você verifique somente quantidades atuais.

    Given there are 3 existent products
    When I create 1 new product
    Then the number of existent products should change to 4

    Given /^there are (\d+) products$/ do |n|
    n.to_i.times { Product.create(…) }
    end

    When /^I create (\d+) new product/ do |n|
    n.to_i.times { … codigo para criar um produto usando a interface da app … }
    end

    Then /^the number of existent products should change to (\d+)/ do |n|
    Product.count.should eql(n.to_i)
    end

  5. Guilherme / jan 29 2009 1:10 am

    Show, ótima explicação!
    muito obrigado cassio!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: