Onde: Hotel Quality Vila Olímpia – Sala Sevilla
Rua Fidêncio Ramos, 420 – Vila Olímpia
São Paulo, SP
[mapa]
Para maiores informações, visite o site do evento
Onde: Hotel Quality Vila Olímpia – Sala Sevilla
Rua Fidêncio Ramos, 420 – Vila Olímpia
São Paulo, SP
[mapa]
Para maiores informações, visite o site do evento
Para instalar, considerando obviamente que você já tem o Rails instalado, digite
gem sources -a http://gems.github.com sudo gem install cucumber webrat rspec rspec-rails
No diretório raiz do seu projeto, digite
script/generate cucumber --rspec
/features.
Na pasta features, cria um arquivo com extensão .feature. Por exemplo, manage_users.feature.
Vamos editar o arquivo e começar com a definição da funcionalidade (ou user story, se preferir):
Feature: Manage users
In order to manage users
Visitors should be able to
sign up and change their own stuff
Atenção para a tabulação. Ela é fundamental para o bom funcionamento da ferramenta.
Em seguida, vamos adicionar os cenários possíveis que se aplicam a essa funcionalidade. Nesse caso podemos pensar nas situações que podem ocorrer para gerenciarmos usuários. Futuramente podemos adicionar mais situações que não foram previstas inicialmente e situações que estão causando comportamentos inesperados na sua aplicação.
Scenario: Add new user
Given the Visitor wants to signup
When the Visitor clicks signup
Then open the signup page
Novamente, atenção para o espaçamento no início de cada linha.
Execute o comando cucumber e você vai ver uma saída parecida com essa:
Feature: Manage users
In order to manage users
Visitors should be able to
sign up and change their own stuff
Scenario: Add new user # features/manage_users.feature:6
Given the Visitor wants to signup # features/manage_users.feature:7
When the Visitor clicks signup # features/manage_users.feature:8
Then open the signup page # features/manage_users.feature:9
1 scenario (1 undefined) 3 steps (3 undefined) 0m0.297s You can implement step definitions for undefined steps with these snippets:
Given /^the Visitor wants to signup$/ do pending end When /^the Visitor clicks signup$/ do pending end Then /^open the signup page$/ do pending end
O texto em amarelo significa que o teste (step na documentação do Cucumber) ainda não foi implementado.
Dentro da pasta features, tems uma chamada step_definitions. Nessa pasta vamos criar um arquivo manage_users.rb, copie o primeiro trecho em amarelo (Given … end) e cole no arquivo. Apague a linha onde está escrito pending e escreva seu código de teste.
Ele vai ficar assim:
Given /^the Visitor wants to signup$/ do #simula a visita do cliente à página principal, onde temos um link escrito "Quero me cadastrar" get '/' end
Ao executar cucumber novamente, veremos a seguinte saída:
(...) Scenario: Add new user # features/manage_users.feature:6
Given the Visitor wants to signup # features/manage_users.feature:7
When the Visitor clicks signup # features/manage_users.feature:8
Then open the signup page # features/manage_users.feature:9
Vemos a linha do step que escrevemos na cor verde, ou seja, o teste passou.
O modo correto de se trabalhar com Cucumber é, assim como em TDD, escrever o teste, dentro de um step, executar e vê-lo falhar. Ao falhar, a linha será apresentada em vermelho. Programe somente o necessário para o teste passar (ficar verde), e passe imediatamente para o próximo step.
Existem formas de se trabalhar com Cucumber em outros idiomas, gerar documentação em HTML e PDF e outros macetes, mas isso vai ficar para um próximo post.
Enjoy
Por que assertNotEquals ao invés de assertDifferent?
Eu poderia ter usado assertDifferent ao invés de assertNotEquals, mas isso poderia nos levar a um problema semântico. Em Java, == (operador de comparação de igualdade) tem um comportamento diferente de equals (método utilizado para checar a igualdade de valores).
No caso de uma String, por exemplo, "foo" == "foo", ou seja, uma String é sempre igual a ela mesma. Porém, se tivermos uma variável foo, contendo “foobar” e outra bar, também contendo “foobar”, estaremos falando de variáveis diferentes, posicionadas em locais diferentes da memória e, na maioria das vezes, com valores também armazenados em endereços diferentes, fazendo com que o uso do operador == retorne falso, mesmo quando ambas contém o valor “foobar”.
Por conta de situações como essa, os objetos Java contém o método equals, que verifica se o conteúdo é igual, e não o endereço onde o conteúdo está guardado. Usando o exemplo do parágrafo anterior, foo.equals(bar) retornaria verdadeiro.
Sendo assim, para comparação entre objetos, assertDifferent deveria checar se o objeto informado é exatamente o mesmo do objeto esperado, o que até hoje ainda não vi acontecer. assertEquals checa se o valor da objeto informado é igual ao valor esperado, o que respeita a idéia inicial de uma asserção.
Por outro lado, em Java existe o conceito de tipos primitivos, que são números, bytes, caracteres e valores booleanos (true, false). Como um tipo primitivo não é um objeto, não existe a preocupação de termos um mesmo valor em endereços de memória diferentes, então uma comparação entre foo=42 e bar=42 é sempre verdadeira. Nesse caso sim o uso de assertDifferent faria sentido, mas causaria uma bela confusão por causa da sujeira que mais e mais asserções redundantes traria. Quando tratamos de números, podemos tanto dizer que 42 é diferente de 0 como podemos dizer também que 42 não é igual a 0. Então fica o assertNotEquals e não se fala mais nisso =)
Se interessou pela idéia? Você encontra os mesmos problemas e quer conhecer mais? A biblioteca está na versão 0.1.1 e pode ser encontrada no Google Code. Críticas construtivas são sempre bem vindas.
Keep testing =)
P.S.: Agradecimentos públicos à minha esposa e ao Fábio Serra pelas revisões nesse post
Código A
assertFalse(foo.equals(bar)); assertFalse(foobar == 2);
Código B
assertNotEquals(foo, bar); assertNotEquals(foobar, 2);
Inicialmente criado apenas como um projeto para me auxiliar no trabalho a escrever testes mais simples de ler, resolvi compartilhar no Google Code a biblioteca, atualmente na versão 0.1.1.
A principal motivação foi de criar novas asserções que melhorem a expressividade, mesmo que redundantes, para que o desenvolvedor não tenha que escrever códigos auxiliares limitados à API padrão do JUnit.
Internamente o próprio JUnit trabalha dessa maneira. Praticamente todas as demais asserções são abstrações sobre assertTrue, no máximo adicionando alguma formatação diferenciada para que o resultado e um teste falho seja legível.
Ao invés de, por exemplo, escrevermos assertFalse(0 == 1), podemos escrever assertNotFalse(0, 1). Ao invés de assertTrue(x > 0), podemos simplesmente escrever assertGreaterThanZero(x) e assim por diante.
Outra vantagem diz respeito à cobertura de testes. Em alguns casos, quando se usa assertFalse(x == 0), o algoritmo que calcula a cobertura vai informar que essa linha foi apenas parcialmente testada. Na verdade queremos apenas testar se x é igual a zero, mas se você persegue os 100% de cobertura, esse tipo de problema obriga a criar código extra apenas para manter a cobertura do código satisfatória. Utilizando assertNotEquals(0, x), ou o teste passa ou não passa, reduzindo para apenas duas as opções possíveis, limpando o código e aumentando automagicamente a cobertura.
Se interessou pela idéia? Você encontra os mesmos problemas e quer conhecer mais? A biblioteca está na versão 0.1.1 e pode ser encontrada no Google Code. Críticas construtivas são sempre bem vindas.
Keep testing
P.S.: Agradecimentos públicos à minha esposa e ao Fábio Serra pelas revisões nesse post
Finalmente tirei férias e vou aproveitar o tempo livre para casar, ter um(a) filho(a), tomar um pouco de sol, escrever um artigo, montar uma palestra e, se sobrar tempo, escrever uma aplicação de OCR só pelo tesão de programar.
Bom final de Agosto e bom início de Setembro a todos e lembrem-se, crianças: Winners write tests
application_controller.rb:
def index
begin
if request.post?
create
elsif request.get?
read
elsif request.put?
update
elsif request.delete?
delete
end
rescue NameError
respond_to do |format|
format.html {render :text => "<h1>Forbidden</h1>", :status => 403}
end
end
end
E no arquivo routes.rb:
map.connect ':controller/:id' # map.connect ':controller/:action/:id'
Como funciona?
Uma aplicaçao Rails RESTful funciona, basicamente, utilizando o método HTTP como verbo e a URI como substantivo.
Ao invés de algo do tipo http://application:3000/user/view/1, com REST você usa http://application:3000/user/1 para pegar os dados, atualizar ou apagar, mudando apenas o método HTTP da chamada.
Porém, por padrão, o Rails entende as requisições como sendo /controller/action/id, por isso a alteração no arquivo routes.rb, para fazer com que o segundo parâmetro seja o ID, já que quem define a action é o próprio método HTTP.
Dependendo do método utilizado, será executada uma action padronizada que será escrita no seu próprio controller, usando o conceito de sobrescrita de métodos. Caso você não escreva qualquer uma das actions, será exibida uma mensagem de erro padrão, permitindo que você foque somente no desenvolvimento do que for necessário.
De volta ao Brasil, e de volta à programação normal.
A propósito, um desafio geek. Refatore o código abaixo:
do {
} while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend);
Simples
“Fazer refactoring sem testes é o mesmo que pegar puta no calçadão e depois dizer ‘eu não sabia’ quando as coisas não saem como esperado.”
Pensamento antigo, mas válido.
Mais duas semanas sem postar, sem dormir e sem comer direito.
E há quem pense que eu viajo para ficar esquiando por aí.