Rodrigo Yoshima
(twitter: @rodrigoy): Técnico em Processamento de Dados, bacharel em Administração de
Empresas e autor, escreveu artigos com renomados autores nacionais e internacionais como
Scott Ambler, Jon Kern e James Shore. Trabalha com projetos de software há 16 anos, tendo
treinado mais de 100 equipes em práticas ágeis no Brasil nas mais variadas empresas e
setores. Atua como instrutor/Coach Agile na Aspercom e escreve no blog Débito Técnico
(http://blog.aspercom.com.br).
Neste artigo apresentamos a evolução do
desenvolvimento de software ágil na sua prática mais
elementar: o desenvolvimento orientado a testes ou
Test-Driven Development (TDD). O objetivo é falar sobre
as evoluções da técnica como o Behaviour-Driven
Development (ou BDD) e as diferenças entre eles.
A cultura
TDD e o BDD
um dia desses Robert Martin, um evangelista de boas
práticas OO, acalorado proponente de desenvolvimento
orientado a testes e um dos autores do Manifesto Ágil de
2001, declarou no Twitter: “Desenvolvimento Orientado a Testes
é a segunda coisa que um programador deve aprender. A primeira
é o Hello World”. Scott Ambler uma vez declarou que Agile é
muito sobre testes. Kent Beck, um dos criadores do Extreme Programming, também autor do Manifesto e co-autor do framework
de testes JUnit disse recentemente em seus screencasts que testes
fazem com que desenvolvedores tenham responsabilidade pela
qualidade do seu código. Para não deixar de citar um autor local,
7JOJDJVT5FMFTVNEPTQJPOFJSPTFN&YUSFNF1SPHSBNNJOHBRVJ
no Brasil, também no Twitter, declarou que seus testes (no formato
de especificações executáveis) “limpavam” o seu código, e quanto
mais especificações executáveis mais limpo seu código ficava.
Se você é uma pessoa no mínimo antenada com o que ocorre
no nosso mundo de TI já terá visto algumas dezenas de vezes
em palestras, podcasts, blogs e revistas, autores declarando uma
verdadeira paixão por testes automatizados. Test-Driven Development é apenas uma técnica ágil de programação, e que se você
não domina o TDD pode até se sentir ofendido com o discurso
inflamado desses “militantes”. Pelo menos, isso aconteceu comigo! Por que será que há esse embate tão acalorado sobre este
assunto? Por que agilistas são tão dogmáticos com relação à TDD?
Por que nos últimos dois anos houve uma crítica tão grande ao
Scrum por exatamente não conter essas boas práticas? O objetivo
deste artigo não é falar sobre a própria prática do TDD, mas sim
sobre como desenvolver uma mentalidade orientada a testes em
programação, falando sobre alguns avanços recentes no método e
nas ferramentas diferenciando TDD e BDD.
As três Leis do Test-Driven Development
É bastante comum palestras e artigos falarem sobre o famoso ciclo
TDD: Red – Green – Refactor. Na revista MundoJ número 29 expliquei no artigo “O ciclo ágil de um dia” detalhes sobre a técnica
TDD. Um ponto que gostaria de ter colocado naquele artigo, mas
ficou de fora, são as três leis do Robert Martin para o TDD. Sei
que é um pouco complicado quando falamos que uma determinada prática tem leis, principalmente em desenvolvimento ágil,
porém, uma das coisas que mais me auxiliaram a desenvolver uma
mentalidade TDD é esta contribuição do Tio Bob (o Robert Martin
também é conhecido como “Uncle Bob”).
21
: : www.mundoj.com.br : :
As três leis do TDD
1.7PDÐOÍPQPEFFTDSFWFSDØEJHPEFQSPEVÎÍPRVFOÍPTFKB
fruto de fazer passar um teste de unidade que falha.
2.7PDÐOÍPQPEFFTDSFWFSOVNUFTUFBMÏNEPNÓOJNPOFcessário para o teste falhar; e erros de compilação são falhas.
3.7PDÐOÍPQPEFFTDSFWFSDØEJHPEFQSPEVÎÍPRVFTFKBBMÏN
do necessário para fazer o teste passar.
Figura 1. Dados amigáveis do perfil.
Essas leis são outra visão do ciclo TDD: elas te ajudam mostrando
coisas que você não pode fazer para seguir o método. Se você seguir
essas regras, vai fazer TDD, mas tenha em mente que elas são somente aquelas “rodinhas” de auxílio que colocamos quando não sabemos
andar de bicicleta. No início elas vão te ajudar bastante, e se você tiver
o mínimo de senso crítico vai compreender o TDD através dessas
leis. Compreender o TDD é sair do paradigma “pensar – programar
bastante – executar – debugar – consertar – automatizar teste (se der
tempo)” e adotar um pensamento “adicionar um teste – fazer o teste
passar rapidamente – refatorar (pensar)”. É realmente bastante duro
você seguir essas leis, e para quem está acostumado a não segui-las,
aplicá-las no início é bem pouco natural. Fora do TDD, programadores correm para escrever código de produção mesmo sem ter bons
requisitos ou sem saber se componentes do seu código realmente
funcionam isoladamente. É muito comum programadores não-TDD
ter um sentimento tipo “só tudo integrado é que importa”, e assim,
depois de escrever muito código de produção, este desenvolvedor
que não segue TDD vai iniciar a aplicação e correr atrás dos problemas, muitas vezes gastando muito tempo debugando e vendo
se o programa cumpre o desejado olhando diretamente nas telas. A
cultura TDD não é assim. A cultura TDD é SABER se seu código realmente funciona a cada passo dado. O rumo da programação se torna
previsível através de ciclos de feedback curtos. Se você realmente
quer compreender e aplicar o TDD, sugiro que faça um quadro com
essas leis e cole na frente do seu micro. Aplique as leis a cada linha de
código que você digita. Compreenda que essas leis são uma simplificação útil que farão você entender como é mais produtivo fazer seus
testes trabalharem para você, melhorando a confiança daquilo que
você faz. Prometo que você verá uma luz.
Como funciona na prática? Mais um exemplo de TDD?
Antes de mais nada, alerto que é difícil escrever artigos e livros sobre
TDD e sua mecânica, pois 90% do processo é mental. Muito falamos
sobre frameworks de testes de unidade e mocking associados à TDD,
entretanto, desenvolvimento orientado a testes é mais sobre a maneira
que o programador pensa durante a sessão de programação. O ciclo
TDD ou as Leis do Uncle Bob são metáforas desse pensamento. Eu
poderia colocar neste artigo mais um exemplo cheio de código passo a
QBTTPQPSÏNEFDJEJGB[FSBMHPEJGFSFOUF7PVEBSVNFYFNQMPEFVNB
pequena funcionalidade desenvolvida comparando o pensamento
TDD com o pensamento não-TDD lado a lado. Espero que dê certo!
Antes disso, vamos colocar alguns requisitos simples para trabalharmos. Muitas vezes quando estou treinando equipes em práticas ágeis
de engenharia com Extreme Programming uso um sistema-exemplo
de rede social (veja as referências para o código-fonte). Na tela do perfil
de usuário da rede social há um pequeno quadro com foto e dados
BNJHÈWFJTEPVTVÈSJP7FKBBmHVSB
22
A funcionalidade é bastante simples. Precisa aparecer: Casado,
Casada, Solteiro, Solteira etc. e também a idade. O único detalhe
é que na classe Usuario as informações estão como sexo, estado
civil e data de nascimento em propriedades separadas. Desta forma precisaremos gerar uma visualização amigável desses dados na
QÈHJOBEPQFSmM7BNPTBPRVBESPDPNQBSBSDPNPTFSJBGFJUP
com TDD e sem TDD.
Pensamento sem TDD
Pensamento com TDD
Implementar o método getEstadoCivilTela na classe Usuario.
Escrever um teste para o caso “Solteiro”. [RED]
Implementar o método getIdade na
classe Usuario.
Implementar o método getEstadoCivilTela que simplesmente retorna hardcoded “Solteiro” [GREEN]
Colocar o servidor no ar.
Escrever um teste para o caso “Solteira” [RED]
Navegar até a tela. Erro NullPointerException! Estado civil é opcional.
Implementar o método getEstadoCivilTela que
simplesmente retorna hardcoded “Solteiro” ou
“Solteira” [GREEN]
Inicia um Debug. Acha o erro. Corrige a
implementação.
Escrever um teste para o caso “Casado” [RED]
Reinicia o servidor.
Implementar o método getEstadoCivilTela que
simplesmente retorna hardcoded “Solteiro” ou
“Solteira” ou “Casado” [GREEN]
Navega até a tela. “Funcionou”.
Refatorar o método para tirar os hardcodes. [GREEN]
(após alguns outros testes) Erro! NullPointerException! Data de Nascimento
também é opcional.
Escrever um teste para o caso “Casada”. Os testes
passam sem precisar implementar nada. Já está
consistente. [GREEN]
Inicia um Debug. Acha o erro. Corrige a
implementação.
Alguma coisa está errada. Essa responsabilidade
de mostrar o estado civil amigável e a idade não
deveria ser responsabilidade do próprio Usuário.
Refatorando... criando a classe EstadoCivilIdade na
camada View. [GREEN]
Reinicia o servidor.
Escrever mais testes verificando casos não cobertos,
dados opcionais etc. implementando a cada novo
teste e refatorando sempre que achar algum código
sujo. [GREEN]
Faz mais alguns testes.
Escrever um teste para que apareça “Casado” para o
usuário Kent Beck na tela de perfil. [RED]
(se der tempo)
Grava um script de testes.
Faz a implementação da tela.
[GREEN] (neste momento o estado civil realmente
funciona)
Escreve um teste para alguém com 30 anos...[RED]
(continua...)
Quadro 2. Comparando “não-TDD” e TDD.
Como podemos ver no quadro 2 as duas abordagens possuem
filosofias completamente diferentes. Na primeira opção sem TDD
você programa contando com a sorte e nunca tem parâmetros verdadeiros para saber se seu código realmente funciona. Os ciclos
de feedback são longos demais. Já na abordagem ágil com TDD a
cada passo dado você SABE que seu código funciona! Cada linha
do código de produção nasceu de uma premissa na forma de um
teste que falha, e assim, o sistema cresce de maneira incremental,
sabendo que a cada passo dado as coisas funcionam. O exemplo
do quadro 2 também nos mostrou que TDD não é somente sobre testes de unidade. Nas ferramentas atuais, como o Jbehave,
Cucumber, Pyccuracy, entre outras, é possível você escrever um
script de testes que é executável e ao mesmo tempo legível pelos
usuários. Além disso, essas ferramentas permitem que você escreva esse script de teste executável antes mesmo da aplicação existir.
Test-Driven Development é uma mentalidade. Test-Driven Development é um nome que carrega nas costas vários outros conceitos
ágeis importantíssimos como Passos de Bebê, Design Incremental,
Arquitetura Emergente, Refactoring, Feedback, Cobertura de Testes, Builds Automatizados e Integração Contínua. Essa é a razão
por que vários agilistas recomendam fortemente o uso do TDD
como uma prática indispensável. O TDD é o resumo da cultura
ágil sobre engenharia de software, e se você não se alinhar com
essa cultura, dificilmente será um desenvolvedor ágil de verdade.
7FKBB-JTUBHFNDPNBDMBTTFEFUFTUF
Listagem 1. Classe TesteEstadoCivilIdade.
//Listagem 1 – Classe TesteEstadoCivilIdade
public class TesteEstadoCivilIdade {
@Test
public void testCasado() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“C”, “M”));
Assert.assertEquals(“Casado, 52 anos”, eci.toString());
}
@Test
public void testCasada() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“C”, “F”));
Assert.assertEquals(“Casada, 52 anos”, eci.toString());
}
@Test
public void testSolteiro() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“S”, “M”));
Assert.assertEquals(“Solteiro, 52 anos”, eci.toString());
}
@Test
public void testSolteira() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“S”, “F”));
Assert.assertEquals(“Solteira, 52 anos”, eci.toString());
}
private Usuario getUsuario(String estCivil, String sexo) {
Usuario usuario = Mockito.mock(Usuario.class);
Mockito.when(usuario.getEstadoCivil()).thenReturn(estCivil);
Mockito.when(usuario.getSexo()).thenReturn(sexo);
return usuario;
}
Durante muitos anos nós escrevemos TDD da maneira exposta
na Listagem 1. Uma mudança significativa nos últimos tempos é
a evolução do TDD para o Behavior-Driven Development. O BDD
veio para complementar e ampliar o TDD.
Especificações ágeis
Antes de falar sobre o tal Behaviour-Driven Development, quero
que mais uma vez você olhe atentamente a Listagem 1. É uma
classe de teste simples do JUnit. Numa classe de teste temos preparação de testes, métodos de testes, asserções e outras coisas de
apoio. Na filosofia TDD a classe ou script de teste é o início de
tudo. E os testes e implementações evoluem passo a passo como
demonstrado no quadro 2.
O problema com o Test-Driven Development é a palavra “Teste”.
Quando falamos teste estamos levando o assunto para o que em
um software deve ser verificado e como pudemos ver nos últimos
parágrafos, TDD é mais que somente testes. Muitas vezes quando
ensinamos sobre TDD as dúvidas “pipocam” sobre como testar, o
que testar e qual a profundidade dos testes. Dan North vivendo
essa mesma crise comum entre muitos agilistas sugeriu alguns
padrões interessantes e cunhou com isso o Behavior-Driven Development (ou simplesmente BDD).
A contribuição de Dan North trouxe para o desenvolvimento ágil
uma linguagem comum de análise de software. Isso envolve não
somente os desenvolvedores, mas também analistas de negócio,
o cliente, os testers e qualquer outra pessoa envolvida. Todos
devem falar a mesma língua. A primeira mudança significativa é
tirar todos os jargões de teste do TDD substituindo-os por termos
mais focados em comportamento (behaviour). Como exemplo, no
BDD nós substituímos a palavra “test” por “deve” (should) e com
isso especificamos os comportamentos esperados do software.
7FKBOB-JTUBHFNDPNPmDBSJBQBSUFEPDØEJHPEB-JTUBHFNOB
abordadem BDD.
Listagem 2. Classe EstadoCivilIdadeSpec.
public class EstadoCivilIdadeSpec {
@Test
public void deveApresentarCasado() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“C”, “M”));
Assert.assertEquals(“Casado, 52 anos”, eci.toString());
}
@Test
public void deveApresentarCasada() {
EstadoCivilIdade eci = new EstadoCivilIdade(getUsuario(“C”, “F”));
Assert.assertEquals(“Casada, 52 anos”, eci.toString());
}
...
}
Na Listagem 2 usamos o termo “Spec” (de especificação) para
identificar qual unidade está sendo analisada. Fora isso, agora
nomeamos os métodos como “deveXXXX()” para indicar o que
23
: : www.mundoj.com.br : :
o componente PRECISA fazer. Essa mudança aparenta ser quase
nada, mas nos ajuda a focar naquilo que o sistema precisa fazer
ao invés do que precisa ser testado. Cada “deveXXX()” que colocarmos nessa classe se torna um requisito executável da unidade
que estou testando. Concluindo, nesta abordagem escrevemos a
ESPECIFICAÇÃO primeiro, focando-se naquilo que é mais importante – aquilo que precisa ser feito agora para que mais valor
seja agregado à solução.
Inicialmente, Dan North começou seus trabalhos evoluindo o
JBehave, seu framework BDD para Java. Sua influência passou
também para a comunidade Ruby que atualmente utiliza fortemente o RSpec e o Cucumber. O RSpec é uma das melhores implementações atuais de BDD e Mocking, se beneficiando bastante
das características dinâmicas da linguagem Ruby. Entretanto, o
BDD não é somente essas mudanças estéticas em testes de unidade. BDD define mais termos em sua linguagem ubíqua. Considere
a especificação constante na Listagem 3.
Listagem 3. Especificação executável.
Como um membro da rede social
Eu posso consultar os dados amigáveis de um perfil
Para fomentar a integração social da empresa
Dado que o usuário Kent Beck é homem, casado e nascido em 13/08/1961
E hoje é 10/08/2010
Quando eu acesso a sua página de perfil
Então eu vejo “Casado, 48 anos”
Dado que o usuário Ana Marta é mulher, viúva e nascida em 25/09/1973
E hoje é 10/08/2010
Quando eu acesso a sua página de perfil
Então eu vejo “Viúva, 36 anos”
A pergunta que sempre faço quando demonstro scripts do JBehave
ËTQFTTPBTÏi7PDÐBDFJUBSJBVNEPDVNFOUPEFTTFTDPNPEPDVNFOtação de requisitos do seu sistema?”. Eu estendo essa pergunta a
você aqui agora! O BDD fornece padrões que nos permitem estabelecer uma linguagem ubíqua de análise do software muito próxima
da maneira como o usuário entende o negócio (podendo inclusive
ser em português como nos mostra o exemplo).
Os termos destacados em vermelho na Listagem 3 (Como, Posso,
Para, Dado, Quando e Então) são termos-chave do BDD. Formalizando e padronizando estes termos comuns, com o BDD você
consegue aproximar usuários, Product Owners e equipe em torno de especificações executáveis claras para todos e que validam
a aplicação sob o ponto de vista do usuário. “Como <um papel>,
posso <desempenhar uma ação>, para <obter algum resultado de
negócio>” – este é o formato padrão de escrita de histórias do usuá-
24
rio. Os passos do script que iniciam com “Dado ….” são premissas
que preparam ambiente ou pré-condições para execução da spec.
Os passos “Quando...” determinam uma ação ou conjunto de ações
desempenhadas pelo usuário. Finalmente os passos “Então...” permitem você avaliar o estado do sistema (como asserções) após o
desenrolar das ações. É tudo muito parecido com o que fazemos
com testes TDD, porém, dentro do formato BDD, tudo é mais claro
para o usuário e outros envolvidos (principalmente aqueles que não
sabem ler código – compare as Listagens 1 e 3). Com o script criado
neste formato, é necessário implementar os steps do JBehave. O
objetivo aqui não é demonstrar o JBehave. O código listado nas
referências possui um exemplo funcional similar.
No mundo ágil grande parte da documentação funcional do sistema está na forma de várias especificações executáveis usando BDD.
É um mito achar que Agile não documenta nada. A diferença está
mais na forma de documentar. Times ágeis de verdade sabem que
documentações apartadas do código tendem a ficar desatualizadas.
Assim, especificações executáveis é uma forma ideal de se manter a
documentação sempre em dia e “amarrada” com a implementação.
A maturidade no TDD é você deixar de debugar código. No BDD
vemos maturidade quando um desenvolvedor recorre às especificações executáveis para ver o que um determinado comportamento
do sistema faz. Além do JBehave temos muitas ferramentas como o
Concordion, Fitnesse e outras.
Conclusão
O objetivo aqui foi demonstrar um pouco o que é a cultura ágil
na ótica do TDD e BDD. As filosofias das duas práticas são bem
correlacionadas, porém, é muito comum TDD ser mais voltado
ao desenvolvedor e o BDD ter um espectro mais amplo, atingindo
todos no projeto inclusive o cliente ou usuários. O BDD tem um
foco maior em testes funcionais e de aceitação.
Isso pode até ser listado como uma das incoerências conceituais
muito comuns no mercado: não estranhe se algum dia você ouvir desenvolvedores falando muito sobre “especificações” numa
equipe ágil. Em desenvolvimento ágil nós especificamos muito!
Porém, ao invés de especulações fúteis guardadas em um documento qualquer, preferimos especificações executáveis e a análise
nos termos do que o BDD nos oferece.
Referências
t4JTUFNBEF3FEF4PDJBM&YFNQMPoIUUQHJUIVCDPNSPESJHPZCFDLBOPTKBWB
t%BO/PSUI*OUSPEVDJOH#%%oIUUQCMPHEBOOPSUIOFUJOUSPEVDJOHCEE
t,FOU#FDL5FTU%SJWFO%FWFMPQNFOUCZ&YBNQMF
Download

A cultura TDD e o BDD