Estou de férias, e eu não ia escrever nada aqui no blog antes de voltar ao trabalho, mas isto merece o post ![]()
A chamada de trabalhos para o Agile Brazil 2010 esta aberta.
Se você tem algo interessante sobre Agile para falar, envie sua proposta de palestra, tutorial ou workshop por este link.
A Agile Brazil 2010 é um evento nacional organizado por representantes das principais comunidades ágeis brasileiras. Junte-se a nós submetendo trabalhos, participando do concurso do logo e divulgando o evento.
Acompanhe as novidades do @agilebrazil pelo Twitter.
O evento vai acontecer em Porto Alegre de 22 a 25 de Junho 2010.
Espero ver alguns de vocês por lá!
Tags: agile, bdd, cucumber, eventos, Java, palestras, rails, Ruby, tdd

Desenvolvimento guiado pelo comportamento da aplicação é o que todos deveriam fazer sempre, de forma bastante resumida é definir com o cliente como a aplicação deve se comportar, escrever um teste automatizado para verificar este comportamento e depois escrever código suficiente para fazer o comportamento da aplicação ficar de acordo com o que o cliente deseja.
A diferença básica do Behaviour Driven Development (de agora em diante simplesmente BDD) para o desenvolvimento orientado por testes, é que o BDD coloca em foco o valor para o negócio que o software vai adicionar. Parece ser uma diferença puramente conceitual mas não é, por exemplo:
A tela inicial deve listar todos os clientes
Não é a mesma coisa que
O método HomeController.index precisa popular a variável @clientes
Claro que não são só estas diferenças, trabalhando mais com BDD percebe-se que ele poupa muito mais trabalho do que o TDD simples (Não que TDD seja simples de se adotar). BDD tem todas as vantagens de TDD e mais algumas, veja as duas listas abaixo:
Test Driven Development:
Behavior Driven Development:
Ou seja, alem de gerar um código com mais qualidade, o BDD poupa trabalho de toda a equipe e o principal, melhora a comunicação, que tanto para quem trabalha com metodologias ágeis quanto para quem não trabalha é extremamente importante e a falta dela é um problema gravíssimo que leva diversos projetos ao fracasso. Na minha opinião, só o fato de melhorar a comunicação já é motivo o suficiente para testar BDD.
Claro que alguns dos benefícios que eu citei dependem do suporte de ferramentas, para ser mais preciso, as User Stories serem o código executável dos testes de aceitação depende de uma ferramenta para ser verdade, e a ferramenta que eu escolhi para isto é o Cucumber, do qual eu vou começar a falar com mais freqüência aqui no blog.
Eu pessoalmente encaro o BDD como uma evolução do TDD, eu sempre tive dificuldades em escrever testes unitários antes do código da aplicação, claro que para bibliotecas é fácil, mas para a interface com o usuário que consome boa parte do código da aplicação é bem complicado escrever testes estilo XUnit, mas quando as user stories se tornaram o código executável dos testes de aceitação da UI (pelo menos os básicos desconsiderando o layout) um novo mundo se abriu para mim e tudo passou a fazer sentido.
para quem não percebeu este sub-titulo é uma brincadeira com a tradução da palavra cucumber que quer dizer pepino
Cucumber é uma ferramenta que torna possível executar histórias em texto puro, ele é uma ferramenta escrita em Ruby que veio para substituir o RSpec Story Runner, e tem diversas vantagens sobre este.
O Cucumber, utiliza Ruby e expressões regulares para definir o que qualquer expressão quer dizer, mas antes de entrar nestes pormenores vamos entender um pouquinho da estrutura básica que o Cucumber define para as User Stories.
Para que seja possível executar uma User Storie utilizando o Cucumber, ela precisa ter uma estrutura básica.
Esta é uma forma de definir a estrutura básica de um arquivo .feature do Cucumber, claro que explicando desta forma estas definições se encaixam em muita coisa, então vamos ser um pouco mais especificos.
O Cucumber define algumas palavras chave para cada uma destas sessões, estas palavras chave podem ser traduzidas para diversas linguas, o primeiro exemplo eu vou colocar em ingês, depois vou mostrar o equivalente em portugues, mais adiante quando entrarmos na parte de configurações do cucumber vamos ver melhor como selecionar a lingua utilizada, e como customizar isto, mas por enquanto fiquemos com as configurações padrão.
Feature: Simple math In order to avoid silly mistakes As a math idiot I want to be told the result of simple math operations Scenario: adition Given I have entered 50 into the calculator And I have entered 70 into the calculator When I press add Then I should see 120 on the screen Scenario: subtraction Given I have entered 60 into the calculator And I have entered 30 into the calculator When I press sub Then I should see 30 on the screen
Este é o exemplo de uma história bem simples que o cucumber pode interpretar, as palavras chave apresentadas são:
Estas mesmas palavras podem ser traduzidas para o portugues como:
Utilizando estas palavras chave, seria possível escrever esta história assim em portugues:
Funcionalidade: Matemática Simples Para evitar erros idiotas Como um completo ignorante em matemática Eu quero que operações simples de matemática sejam resolvidas para mim Cenário: adição Dado que eu digite 50 na calculadora E que eu digite 70 na calculadora Quando eu precionar "Adicione" Então eu devo ver 120 na tela Cenário: subtração Dado que eu digite 60 na calculadora E que eu digite 30 na calculadora Quando eu precionar "Subtraia" Então eu devo ver 30 na tela
O legal é que esta histórinha poderia ser escrita por um usuário, as únicas regras reais são:
A estrutura que utilizei na descrição da funcionalidade não esta descrita em palavras chave por que ela não é interpretada pelo cucumber, é apenas uma descrição e o formato pode variar um pouco.
Mas por que utilizar esta ferramenta para testes em vez de qualquer outra?
Por que utilizar o cucumber como ferramenta de testes vai viabilizar uma abordagem BDD no desenvolvimento do seu sistema, e que isto vai te poupar muito dinheiro.
Acho que era isto, falei um pouco de BDD, um monte de Cucumber e acho que consegui mostrar a idéia geral, mas isto vai ficar um pouco mais claro nos próximos posts sobre o Cucumber.
PS.: parabéns pela paciência se você leu até aqui

O ano de 2009 é especial para o Grupo de Usuários de Métodos Ágeis do RS
(GUMA) pois é nele que completamos nossos primeiros 5 anos de vida vinculados
à Sociedade dos Usuários de Informática e Telecomunicações do RS (SUCESU-RS). Para comemorarmos esta data, tão especial para nossa comunidade de agilistas,
preparamos um evento especial em parceria com o Software Process Improvement Network de Porto Alegre (SPIN-POA) que reúne colegas e convidados em dois dias de palestras, mini-tutoriais e debates: o Porto Alegre Agile Weekend 2009!
E o segundo motivo é para avisar que vou fazer um mini tutorial no evento, o título vai ser: Implementando com Rails as Histórias de Usuários
A idéia básica do tutorial é chegar com uma “User Story” em texto, passar ela pro Cucumber, escrever alguns testes e depois implementar o sistema para fazer os testes passarem. E depois disto tudo, utilizar a própria “User Story” transcrita para o Cucumber como aceptance test.
Basicamente um exemplo de BDD com Rails.
E aproveitando a palestra no evento, vou sortear duas copias do livro “Ruby On Rails : Desenvolvimento fácil e rápido de aplicações web“. Uma no final da minha palestra e uma no final do evento (se tiver bastante gente na minha palestra de repente sorteio 3 copias em vez de duas).
É issai galera, espero vocês no Agileweekend.
Tags: bdd, evento, palestra, ruby on rails
Não, eu não quero que vocês gerem o código dos testes, isto iria apenas criar testes inúteis!
Mas eu acho muito chato ter certeza de que todos os possíveis passos de um arquivo txt com a user story estão presentes nos passos definidos no arquivo .rb
Claro que isto não esta considerando reutilização de passos através de Mixins ou outras técnicas semelhantes, mas este script me ajudou bastante ja, então resolvi compartilhar ele para quem estiver interessado
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | name=ARGV[0] all = [] last = "" f = File.new(name) f.each_line do |l| l.strip! l,command,params = *(/^(When|Then|Given|And) (.*)/.match l) if l command = last if command == "And" last = command params = params.gsub /"/, '\"' all << "#{last} \"#{params}\" do\n pending\n end" end end all.uniq!.sort! sym_name = name[File.dirname(name).length+1..-5] puts %Q{require 'stories/helper' steps_for(:#{sym_name}) do } all.each { |l| puts " #{l}"} puts %Q{end with_steps_for :#{sym_name} do run "\#{dir = File.dirname(__FILE__)}/#{sym_name}.txt" end } |
eu salvei este código em um arquivo txt_to_steps.rb, e para utilizar basta executar:
ruby txt_to_steps.rb
Um exemplo de user story (utilizando o formato do RSpec) seria este texto:
Story: new user As a company employee I want to register in the CRM So that I can see and manage company contacts Scenario: user with no access to the system Given the username user1 And the password mypassword When the login form is submited Then the login form should be shown again Scenario: user registration Given the username user1 And the password mypassword And the email user1@company.com When the registration form is submited And there is no other user with the same e-mail or email Then the registration should be OK And the user should be redirected to / Scenario: repeated user registration Given the username user1 And the password mypassword And the email user1@company.com When the registration form is submited And there is already another user with the same name or email Then the registration should fail And the registration form should apear again Scenario: existing user login Given the username user1 And the password mypassword When the login form is submited Then the user should be redirected to /
O txt_to_steps pode ser melhorado para tentar identificar alguns padrões, mas como esta agora ja me poupou bastante trabalho ![]()
Sei que não é o código ruby mais limpo que vocês ja leram, mas para algo escrito em 5 minutos até que ficou legal
Se ajudar mais alguem, a única exigência é deixar um comentário aqui dizendo o que poderia ser melhorado no script
PS.: sei que o blog anda meio parado demais, mas é por um bom motivo, acho que daqui a um mes aproximadamente volta tudo ao normal e eu posso contar aqui o motivo deste tempo quase sem posts
Publiquei eles só no meu blog em ingles, mas acho que vale o link aqui pelo menos …
Para quem é tão preguiçoso quanto eu, eu criei um plugin para o rails que reutiliza tudo (ou quase) o que você ja definiu na migration para gerar o model, o nome do plugin é mydry.
E para quem trabalha com Grails, e estava triste até agora por não poder utilizar BDD de forma fácil, eu criei o plugin easyb-test que permite utilizar o easyb para escrever testes para a sua aplicação Grails.
Se quiserem um exemplo de como usar este plugin do easyb, é só olhar o material da minha palestra do FISL deste ano.

Ok, mas como deixar de perder tempo escrevendo a tal da documentação?
A solução que me vem a cabeça é BDD, ou seja, Behavior Driven Development, junto com a tão falada, documentação executável …
Mas isto existe?
Sim existe, e o pessoal doRuby On Rails já esta bastante acostumado com isto, utilizando o RSpec.
Mas este post não é sobre Ruby On Rails e nem sobre RSpec, e sim sobre o Easyb, que é uma biblioteca para escrita de “Documentação Executável” utilizando Groovy, muito fácil de utilizar para testar código Java …
Para começar com a brincadeira, vou ir ao site do projeto e fazer o download do último release (atualmente 0.7)
criar um projeto java novo no eclipse.
criar uma pasta de nome lib e copiar os seguintes jars para a pasta:commons-cli-1.1.jar easyb-0.7.3.jar groovy-all-1.5.0.jar (todos vem no zip do easyb)
criar mais uma source folder de nome behavior
(O meu eclipse esta com o plugin para edição de código groovy instalado)
Crio um build.xml para executar os testes (ainda não existe um plugin para o eclipse, mas sinceramente, não achei necessário)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?xml version="1.0" encoding="UTF-8"?> <project name="project" default="run_tests"> <path id="all.path"> <pathelement location="${basedir}/bin" /> <pathelement location="${basedir}/bin-groovy" /> <fileset dir="${basedir}/lib" includes="**/*.jar" /> </path> <taskdef name="easyb" classname="org.disco.easyb.ant.BehaviorRunnerTask" classpathref="all.path" /> <!-- ================================= target: default ================================= --> <target name="run_tests" description="--> description"> <easyb failureProperty="easyb.failed"> <classpath> <path refid="all.path" /> </classpath> <behaviors dir="${basedir}/behavior"> <include name="**/*Story.groovy" /> <include name="**/*Specification.groovy" /> </behaviors> <report location="story.txt" format="txtstory" /> <report location="behavior.txt" format="txtbehavior" /> <report location="easyb.xml" format="xmleasyb" /> </easyb> </target> </project> |
Este build.xml declara a tag do easyb e executa todos os testes, alem de gerar os relatórios, que vocês vão gostar bastante …
Os tipos de relatórios variam bastante de acordo com a versão do easyb, a equipe de desenvolvimento esta melhorando bastante isto, eu estou utilizando aqui a versão do trunk do subversion, se quiserem baixar a mesma versão que uso aqui, podem baixar via git deste repositório : git clone git://github.com/urubatan/easyb.git
(Eu fiz um fork do projeto para adicioar algumas features, e ja estou enviando os patches para os desenvolvedores)
Agora vamos começar a brincadeira ![]()
Dentro do diretório behavior vou criar um diretório de nome users e um arquivo de nome PermitirRegistroDeUsuariosStory.groovy dentro deste último.
Ou seja, este arquivo vai conter os testes, e a documentação das regras de negócio sobre a criação de novos usuários no sistema …
Quero ter uma breve descrição da história nos meus relatórios, então adiciono uma tag description no inicio do arquivo (esta tag por enquanto só existe no meu fork).
1 2 3 | description """Qualquer novo visitante do site deve poder se registrar no sistema,
desde que as regras de validação definidas sejam atendidas.
Um usuário registrado podera alterar a sua senha sempre que desejar, mas a senha devera atender aos padrões minimos de segurança""" |
As histórias são compostas basicamente de 4 comandos:
scenario – define um cenário da história, ou seja, um bloco contendo os outros 3 comandos
given – define a situação inicial
when – define um gatilho, uma ação do usuário que ira disparar alguma ação do sistema, este comando é opcional
then – define o que o sistema deve fazer, e o que vai ser testado
alem destes existe o “and” que atualmente é um comando que não executa nada, serve apenas para deixar o código mais legivel, mas isto vai mudar na próxima versão, o and vai repetir o comando anterior, semelhante ao que acontece no RSpec.
Conhecendo estes comandos podemos começar a escrever a nossa documentação ![]()
Vou adicionar alguns cenários a minha história e alguns detalhes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | scenario "Um novo usuário precisa confirmar a senha", { given "Um novo usuário", { } when "O usuário preenche o nome de usuário e a senha, mas sem confirmar a mesma", { } then "A validação do usuário deve falhar", { } when "O usuário preenche o nome de usuário e confirma a senha corretamente", { } then "A validação do usuário deve passar",{ } } scenario "Um nome de usuário deve ser único no sistema", { given "Um usuário com o formulário preenchido corretamente", { } then "O registro deve criar o usuário no sistema", { } when "O nome de usuário ja existe no sistema", { } then "O registro do usuário deve falhar",{ } } } |
Com isto, podemos ver no diretório do projeto os seguintes arquivos criados:
behavior.txt que até o momento esta em branco
story.txt que contem o seguinte texto:
Story: permitir registro de usuarios
Qualquer novo visitante do site deve poder se registrar no sistema,
desde que as regras de validação definidas sejam atendidas.
Um usuário registrado podera alterar a sua senha sempre que desejar, mas a senha devera atender aos padrões minimos de segurança
scenario Um novo usuário precisa confirmar a senha
given Um novo usuário
when O usuário preenche o nome de usuário e a senha, mas sem confirmar a mesma
then A validação do usuário deve falhar
when O usuário preenche o nome de usuário e confirma a senha corretamente
then A validação do usuário deve passar
scenario Um nome de usuário deve ser único no sistema
given Um usuário com o formulário preenchido corretamente
then O registro deve criar o usuário no sistema
when O nome de usuário ja existe no sistema
then O registro do usuário deve falhar
e easyb.xml que contem uma versão XML dos dois relatórios combinados …
Se você prestar atenção no story.txt, substituindo as palavras em ingles que fazem parte da DSL do easyb, por palavras em portugues, esta é uma ótima documentação para ser validada com o cliente, e com a garantia de que não vai ficar desatualizada por que este é o código fonte dos testes, por tanto sera alterado sempre que a regra de negócio for alterada …
Como assim?
Vamos seguir trabalhando no sistema, agora que ja temos o “behavior”, ou seja, o comportamento definido, vou adicionar algum código a esta história que devera ser testada sempre junto com o build automatizado do sistema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package users; description """Qualquer novo visitante do site deve poder se registrar no sistema, desde que as regras de validação definidas sejam atendidas. Um usuário registrado podera alterar a sua senha sempre que desejar, mas a senha devera atender aos padrões minimos de segurança""" scenario "Um novo usuário precisa confirmar a senha", { given "Um novo usuário", { user = new User() } when "O usuário preenche o nome de usuário e a senha, mas sem confirmar a mesma", { user.setName "validName" user.setPassword "aPassword" } then "A validação do usuário deve falhar", { ensureThrows(PasswordValidationException){ user.validate() } } when "O usuário preenche o nome de usuário e confirma a senha corretamente", { user.setName "validName" user.setPassword "aPassword" user.setPasswordConfirmation "aPassword" } then "A validação do usuário deve passar",{ user.validate() } } scenario "Um nome de usuário deve ser único no sistema", { given "Um usuário com o formulário preenchido corretamente", { user = new User('userName','password','password') userManager = UserManager.getInstance() } then "O registro deve criar o usuário no sistema", { userManager.create(user) } when "O nome de usuário ja existe no sistema", { user = new User('userName','otherPassword','otherPassword') } then "O registro do usuário deve falhar",{ ensureThrows(UserAlreadyExistsException){ userManager.create(user) } } } |
Pronto, este é todo o código desta história, e foi apenas o que eu escrevi até agora.
Agora preciso criar as classes User, UserManager e implementar os métodos corretamente, para que os testes passem., por que no momento os testes não estão nem rodando, ja que estas classes não existem ainda …
Criada a classe User:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package users; public class User { private String name; private String password; private String passwordConfirmation; public User() { super(); } public User(String name, String password, String passwordConfirmation) { super(); this.name = name; this.password = password; this.passwordConfirmation = passwordConfirmation; } public String getName() { return name; } public void setName(String userName) { this.name = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPasswordConfirmation() { return passwordConfirmation; } public void setPasswordConfirmation(String passwordConfirmation) { this.passwordConfirmation = passwordConfirmation; } public void validate() { throw new RuntimeException(); } } |
E a classe UserManager:
1 2 3 4 5 6 7 8 9 10 11 | package users; public class UserManager { private static UserManager instance = new UserManager(); public static UserManager getInstance(){ return instance; } public void create(User user){ throw new RuntimeException(); } } |
Os testes agora executam mas falham.
Implementando corretamente o método “validate” da classe User, ja temos 2 testes passando:
1 2 3 4 | public void validate() throws PasswordValidationException { if(password==null || passwordConfirmation==null || !password.equals(passwordConfirmation)) throw new PasswordValidationException(); } |
E agora, finalizando a implementação do método create da classe UserManager.
1 2 3 4 5 6 7 8 | public void create(User user) throws InvalidUserException, PasswordValidationException, UserAlreadyExistsException{ if(user==null) throw new InvalidUserException(); user.validate(); if(users.containsKey(user.getName())) throw new UserAlreadyExistsException(); users.put(user.getName(), user); } |
Todos os testes passam, e o nosso código funciona perfeitamente.
Claro que a DSL do easyb é bem mais completa que isto, é possível testar apenas a especificação de uma classe em vez de uma história completa (eu uso histórias para as regras de negócio importantes para o cliente, e especificações para classes internas do sistema), existem diversos “ensure” para validar o estado desejado, tudo isto vocês podem ver na página do projeto: http://www.easyb.org
E como eu falei antes, este é o código dos testes do sistema, desta forma, quando o usuário solicitar uma alteração, se altera o teste, depois se altera o código.
Quando o seu chefe pedir para você documentar o código escrito até agora, você executa os testes, e copia os txts gerados e envia para ele por e-mail.
Os testes você seria obrigado a escrever de qualquer forma, ja que acredito eu que se você leu até aqui, você acredita que testes de código são importantes.
Como você iria escrever os testes de qualquer forma, agora você precisa fazer uma coisa a menos, que é documentar aquilo que escreveu, por que algum dia você vai querer tirar férias, e outro desenvolvedor vai precisar alterar o código que você escreveu
Depois de ler até aqui, o que você acha do easyb?
O que você acha de Behavior Driven Development?
Você concorda comigo, quando digo que o easyb tornou possível BDD em Java? (Sim eu sei que existe o JBehave, mas eu acho o trabalhoso demais, e sei que é possível trabalhar com BDD utilizando qualquer xUnit, mas ai eu perco muito da vantagem da documentação executável)
Você vai pelo menos testar isto no seu próximo projeto?
Eu estou escrevendo alguns pequenos patches e enviando para eles sempre que sinto falta de alguma coisa, e fora isto o projeto vai ter diversas melhorias bastante significativas em pouco tempo!
Parabens aos desenvolvedores, e viva o Groovy, tornando o desenvolvimento Java mais divertido e menos trabalhoso!