rvf, software e mitos

Posts Tagged ‘desenvolvimento agil

Não, eu não sou nenhum guru em TDD, mas aos poucos estou notando a vantagem desta técnica que agrega muito mais do que simples junits ao seu projeto. TDD te fornece na verdade outra abordagem: o desenho do seu software de uma maneira mais desacoplada e coesa.

... um pouco de história: Era uma vez um jovem desenvolvedor empolgado em introduzir técnicas de testes ágeis em sua empresa, porem, foi barrado pelo seguinte motivo: Testes (senão, os feito por testadores) são uma segunda opção (precisa dizer que a primeira é o cronograma?).  Esta é a visão dos gerentes/gestores de projetos que não conhecem TDD. A única coisa que sabem a respeito é de que TDD se testa primeiro. Na verdade, alguém que pensa assim, não sei nem como chega a este raciocínio, pois duvido muito que eles entendam o que dizem -i.e. como assim testar primeiro algo que ainda nem existe?-

TDD, não deve ser considerado como uma prática de teste -apenas-, mas sim como uma técnica poderosa de desenho/construção de aplicações OO, E, DE QUEBRA, tu ganhas o teste de unidade. Simples assim. Para cada caso, um teste que falha, onde sua história só termina quando ele passa. Por completo! A principio a idéia parece um pouco radical, mas é isso mesmo: você deve construir um teste que falhe primeiro, (nem que seja para testar se o seu JUnit está presente no seu classpath!), para, então, você ir incrementando aos poucos, refatorando, e criando as classes, métodos, xpto…  que sejam necessários para o mesmo passar e testando. Vamos brincar com  isso em um exemplo simples, de Conta Corrente:

Criamos um novo projeto, e nele, um novo teste unitário (estou utilizando o JUnit 4):

package com.wordpress.robsonvf;

import org.junit.Assert;

import org.junit.Test;

public class ContaCorrenteTest {

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

Assert.assertTrue(false);

}

@Test

public void umSaqueDeveDiminuirOSaldoNoValorDoSaque() {

Assert.assertTrue(false);

}

@Test(expected=RuntimeException.class)

public void umSaqueSoEhPossivelSeTemSaldo() {

}

}

Esta é a abstração mais simples possível de conta corrente que consegui chegar, creio que para o exemplo seja suficiente. Rode este teste, e veja-o falhar descaradamente e sem dó: o primeiro passo rumo a TDD foi dado. O segundo é fazer isso tudo funcionar! Notaram que, quando eu disse “abstração mais simples possível de conta corrente” ficou claro que, eu comecei a desenhar meu software antes mesmo de criar uma única classe de negócio sequer? E ainda tem gente que duvida que simples testes unitários são tão claros quanto montanhas de documentos.

Vamos trabalhar então no método: umDepositoDeveAumentarOSaldoNoValorDoDeposito, o que este método precisa para viver numa conta corrente? Na minha abstração primária, ficou assim:

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

ContaCorrente cc = new ContaCorrente(200d);

double saldo = cc.getSaldo();

cc.deposita(100d);

Assert.assertTrue(saldo+100d == cc.getSaldo());

}

Precisa explicar? uma nova sessão de conta corrente, um registro histórico do saldo atual com o qual a conta foi criada, um deposito, e uma verificação: o saldo esperado é igual ao saldo da conta? Você já consegue rodar este teste? Não? Está esperando o que então para criar a classe ContaCorrente e os métodos que o sua IDE chora sem parar por eles não existirem? Se utiliza o eclipse/netbeans, basta ir clicando nos erros e gerando a classe, construtores, métodos…, faltaria só implementar. Este é o seu exercício antes de continuar com a leitura.

Implementou? Rode o teste agora. Passou? Ótimo! Seu primeiro teste usando TDD foi concluído com sucesso. Se quiser, faça isso com os outros métodos até todos passarem. (recomendo fazer isso antes de ver, logo abaixo, o restante do código).

O teste pronto:

package com.wordpress.robsonvf;

import junit.framework.Assert;

import org.junit.Test;

public class ContaCorrenteTest {

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

ContaCorrente cc = new ContaCorrente(200d);

double saldo = cc.getSaldo();

cc.deposita(100d);

Assert.assertTrue(saldo+100d == cc.getSaldo());

}

@Test

public void umSaqueDeveDiminuirOSaldoNoValorDoSaque() {

ContaCorrente cc = new ContaCorrente();

cc.deposita(100);

cc.saque(90d);

Assert.assertTrue(10 == cc.getSaldo());

}

@Test(expected=RuntimeException.class)

public void umSaqueSoEhPossivelSeTemSaldo() {

ContaCorrente cc = new ContaCorrente();

cc.deposita(200);

cc.saque(201);

}

}

A minha classe ContaCorrente (aposto que a sua ficou igualzinha!):

package com.wordpress.robsonvf;

public class ContaCorrente {

private double saldo;

public ContaCorrente() { }

public ContaCorrente(double d) {

this.saldo +=d;

}

public double getSaldo() {

return this.saldo;

}

public void deposita(double d) {

saldo +=d;

}

public void saque(double d) {

if ((saldo-d) < 0)

throw new RuntimeException(“Saldo insuficiente!”);

saldo -= d;

}

}

Aprimore mais a lógica de negócio desta classe, envolva outras classes, e não se esqueça: primeiro o teste, depois a especificação do negócio, e por fim, a implementação… E FUNCIONANDO…

Este post foi apenas um simples estimulo para quem está querendo aprender TDD, assim como eu. Espero que você adote esta pratica para seus novos algoritmos, e, que convença seu chefe de que TDD não é apenas teste unitário avulso, mas, sim, uma nova maneira de se pensar na hora de sair construindo software.

Até a próxima.. e que venha a Agile Weekend aqui em poa 🙂

Anúncios

Depois de um tempo sem postar (puderas, semanas de finalização de versões, instalações e suporte ao teste lá no trabalho, sim, tudo isso em paralelo e não necessariamente se referem ao mesmo projeto. Já disse que começou a faculdade?), venho deixar uma lembrança aos dois ou três (obrigado mãe!) navegantes que tenho por aqui: Sabe aqueles projetinhos simples, que seu chefe tirou lá do fundo do baú, que ninguém queria fazer e você foi o felizardo em “matar no peito”? É dele que quero falar hoje.

Quando você está envolvido num novo projeto, principalmente nas vezes em que você responderá pela arquitetura do mesmo, te vem na cabeça milhares de idéias que –a principio- estão fora do escopo do mesmo (e do cliente), em conjunto com as idéias, surge uma nuvem de frameworks na sua mente, e o tesão de integrá-los, utilizando o maior numero possível de jars, preferindo a escolha de um framework de validação para o “isBlank” (créditos ao Alex) ao invés de implementar a droga do método e, de quebra, tudo isso bombando com aspectos e injeções de dependências na testa. Pense sobre isso, e reflita: Você precisa disso tudo? Você vai precisar daquela coisa que você fez apenas para “SE UM DIA…” mesmo? Não e não!

Projetos simples devem ficar simples. Quanto mais simples ele ficar, melhor pra você, pro seu cliente e para o futuro desenvolvedor que irá dar manutenção no mesmo, pois não precisará aprender aquele bolo de tecnologias que você pensou em empurrar no seu projeto. Faça uso de ferramentas que vieram para facilitar, desde frameworks para desenvolvimento rápido e ágil: Grails, Django, Rails, .. até mesmo as boas e velhas IDEs, como o NetBeans, que já gera uma aplicação JSF a partir de entidades JPA, completinha e bonitinha. Dentre várias outras opções. Keep It Simple, Stupid!

É notável a quantidade de pessoas que acham que antes de fazer um sistema, DEVE ser feito o seu modelo UML. Ok, se isso realmente funcionasse seria tudo muito bonito, um mar de flores. Mas não é bem por aí. Acredito que estas pessoas não entenderam de fato a razão da UML existir.

A UML é (como seu nome já diz), uma linguagem unificada! Ela só serve para você não precisar conversar com o seu cliente (que não entende bulufa da parte técnica) sem ter que mostrar código pra ele. Com uma linguagem de fácil entendimento, vocês podem chegar a modelos de sistema em conjunto com o cliente, onde até ele mesmo poderá modificar o modelo.

Quando a UML vai importar para você, então? Ela será importante na fase de entendimento do seu sistema, para na hora de desenvolver o produto, você considere-a como seu requisito para definir relacionamentos, comportamentos e os atributos de uma maneira clara. Mas, esqueça que ela será uma dependência para seu código, isso acarretaria em ter que manter o seu modelo UML sincronizado com o sistema, o que nem sempre será possível – ou você acha que depois de pronto, o martelo é batido e nada mais muda?

Outro pecado que fazem é criar o modelo UML, codificar de uma maneira diferente e mesmo assim, TER QUE ATUALIZAR O UML. Qual o motivo disso? O desconhecimento. O simples prazer de ter na cabeça que o seu sistema tem um modelo. Isso é inútil e só vai fazer você perder tempo.

Algumas idéias para resolver este problema, é o focar o segmento do sistema em DSLs (linguagens especificas de domínio), onde num mundo ideal, você pudesse modelar o negócio do seu sistema diretamente no seu código, mas isso já é assunto para um outro post.

Não burocratize o desenvolvimento de software, de preferência ao ágil. Só use UML, quando realmente ela poderá agregar em algo. Se tiver melhores opções para o seu lugar, de preferência a estas outras opções, diminuindo assim um problema a menos para se preocupar.


Anúncios