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