Archive for Janeiro 23rd, 2009
How to test file uploads with Cucumber
In a previous article (in portuguese, but you can always use Google Translate to take a look at it) I showed how to use Cucumber to create functional tests for your web applications. Today I’ll show how to use Cucumber to test file uploads. I’ll use a Rails application as our example, but in general the concepts presented here could be applied to test file uploads in any web application.
Let’s consider a ProductsController controller with an get_csv_file action, which purpose is to receive a csv text file with and record the data in each line as a new instance of the Product class. What could be the expected behaviour of such action? Let’s describe it with a Cucumber feature:
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
We have two basic scenarios, one for a correct file and another for a bad one. We don’t have any code yet for the steps Cucumber will execute when running the tests, so let’s do it. I’ll show just the steps for the file uploads.
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
The idea here is to create two files, one good and one invalid, and put them somewhere inside your application. I put them inside features/upload-files, you can place them wherever you like. The attach_file method received a symbol with the name of the field used for the upload and the path for a file in your disc. After associating the field with the path for the file, all we’ll have to do is “to click” at the submit button.
Here’s the code of the action I wrote
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
With this solution you can test any kind of file uploads. A good example would be to create restrictions about the type of the files that can be uploaded, restrictions about the dimensions of uploaded images and so on.
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.


