Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

25 Mar 08 Legal, mais de 1000 assinantes do feed do blog :D

Fiquei feliz hoje quando acessei o Feed Burner!

subscribers.PNG

Hoje, existem 1002 pessoas que são notificadas automagicamente de qualquer asneira que eu escrevo aqui :D

Muito obrigado aos leitores, espero que estejam recebendo coisas úteis através deste feed :D

24 Mar 08 Vou palestrar no FISL este ano :D

fisl-logo.jpeg

Acabei de ver que saiu na terceira lista de palestras aprovadas do FISL a minha palestra: Grails – Agilidade, produtividade e código bonito ao alcance de todos!

Então nos vemos no FISL, espero que os que aparecerem por la assistam a minha palestra, vai estar bem legal :D
Não vai ser só aquele CRUD básico que é apresentado em todos os lugares, estou preparando um esquema bem legal para vocês :D

<updated>
Acabou de ser publicado na grade de horários, a palestra vai ser dia 19 de abril as 16h :D
Espero que pelo menos um ou dois de vocês apareçam por la para que eu não fique sozinho na sala :D
</updated>

Tags: , , , ,

24 Mar 08 Pare de perder tempo escrevendo documentação do código que você escreveu (parte 1)

time_graphic.jpg

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 :D
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 :D
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 :D

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!

Tags: , ,

05 Mar 08 Melhorando o “try()” do Ruby, a maneira “Groovy” :D

Bom, desta vez não vou traduzir o meu post, então deem uma olhadinha no meu blog em ingles: A better “try()” for Ruby, why not do the Groovy way?

Mas um resumo rápido:
Acho que todos concordam que é bem chato nas views por exemplo em uma aplicação Ruby escrever:

<%= @person.name if @person %>

Fazer isto um monte de vezes, ou usar o operador ternario, começa a ficar chato com o tempo …
então algumas pessoas sugeriram utilizar:

<%= @person.try(:name) %>

Eu acho que isto fica mais estranho do que o if.
Então resolvi escrever este post: A better “try()” for Ruby, why not do the Groovy way?

Tags: ,