
Não, eu não estou completamente louco, e não acho que documentação para o código escrito seja uma coisa completamente inútil.
Mas eu acredito que documentação desatualizada é pior do que nenhuma documentação …
E não estou incentivando a escrita de código completamente sem documentação …
E por último, só para constar, eu estou falando de documentação da regra de negócio implementada e não o manual do usuário …
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!
Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!