Introdução a tdd

2,190 views

Published on

Palestra de TDD com uma visão geral bem completa que utilizamos em um treinamento interno na Qualidata.

Published in: Technology

Introdução a tdd

  1. 1. Introdução ao Desenvolvimento Orientado a Testes (Test-DrivenDevelopment - TDD)<br />Fabrício Matos<br />Nov/2008<br />
  2. 2. TDD é uma técnica de desenvolvimento de software cujo processo é formado por pequenas iterações para o desenvolvimento de uma nova funcionalidade, começando pela implementação de um caso de teste, depois pelo código necessário para fazer o teste passar, e finalmente pela refatoração do código visando melhor acomodar as mudanças feitas.<br />Não é um método para testar software, mas para construir software.<br />TDD vem do conceito de “test-first programming” do XP (Extreme Programming), mas acabou ganhando tanto interesse, que hoje tem sido adotado independente do XP e das técnicas de programação ágil;<br />Introdução<br />Página 2<br />
  3. 3. Objetivo do TDD: <br />“cleancodethat works”<br />código limpo que funciona<br />“Mantra” do TDD: vermelho-verde-refatorar<br />Codifique o teste;<br />Faça ele compilar e executar (não deve passar - vermelho);<br />Implemente o requisito e faça o teste passar (verde);<br />Refatore o código;<br />Introdução<br />Página 3<br />
  4. 4. Garante a existência de testes unitários completos e atualizados, que:<br />Eliminam o medo de alterarmos alguma coisa que funciona (testada manualmente), e acabarmos introduzindo algum problema;<br />Nos permite utilizar refatoração (substituir uma implementação por outra equivalente) de forma muito mais agressiva devido à facilidade dos testes verificarem o resultado.<br />Diminui a quantidade de erros por linha de código (código-fonte de mais qualidade)<br />Testes unitários servem como especificação de como os componentes do sistema funcionam;<br />Nos leva a produzir componentes de software mais desacoplados, para garantir o isolamento dos testes, o que acaba favorecendo o projeto do sistema.<br />Principais Benefícios do TDD<br />Página 4<br />
  5. 5. Nosso foco é em produtos, e não em serviços, e os produtos precisam acompanhar as mudanças dos processos dos clientes, logo nossos sistemas estarão em constante evolução. Testes automáticos nos ajudarão pois: <br />Evitarão efeitos colaterais nas futuras alterações.<br />Documentarão bem o sistema para novos profissionais contratados.<br />Permitirão a liberação de releases de muito maior qualidade.<br />Erros reportados serão reproduzidos via caso de teste, corrigidos e nunca mais retornarão pois estarão cobertos pelo teste automático;<br />Importância para Qualidata<br />Página 5<br />
  6. 6. A Metodologia TDD<br />Estudo de Caso: Aplicação do TDD na IBM<br />Tese de Doutorado - MirkeyaCapellán(2006)<br />Measuring Productivity and Code Quality: Test-Driven Development vs. Waterfall<br />Construção de Casos de Testes com NUnit<br />Considerações Finais<br />Organização da Apresentação<br />Página 6<br />
  7. 7. A Metodologia TDDBaseado principalmente emKent BeckTest-DrivenDevelopmentByExample, 2003<br />Página 7<br />
  8. 8. Testes de Unidade: Testa uma unidade mínima do software;<br />Testes de Integração: Testa a comunicação entre os módulos;<br />Testes de Sistema: Testa todo o sistema para verificar se atende aos requisitos;<br />Testes de Integração de Sistema: Testa a comunicação do sistema com outros sistemas com os quais interage;<br />Testes de Aceitação: Testes realizados pelo usuário final para verificar se o sistema faz o que ele espera;<br />Tipos de Testes<br />Página 8<br />
  9. 9. A Metodologia TDD é conduzida através dos “testes do programador”.<br />Frequentemente esses testes são chamados de “teste de unidade”, mas esse nem sempre é o caso (pode ser teste de integração).<br />É importante destacar que não se trata dos testes de sistema (caixa preta) nem os de aceitação (feitos pelo usuário final)<br />Tipos de Testes<br />Página 9<br />
  10. 10. A “Espiral da Morte” do Teste<br />Página 10<br />O ciclo mortal do “estou sem tempo para testar”:<br />
  11. 11. 1. Construa testes isolados uns dos outros<br />Um caso de teste não deve depender do sucesso de outro para funcionar;<br />Deve ser possível executar um caso de testes isoladamente, sem executar nenhum outro;<br />2. Comece definindo uma “TestList”<br />De modo geral para uma mesma classe ou método a ser testado, existirão diferentes casos de teste. Liste-os primeiro (brain-storm);<br />Provavelmente ao longo do desenvolvimento você adicionará novos casos de teste à lista;<br />Princípios<br />Página 11<br />
  12. 12. Mas o que testamos?<br />Fluxos Condicionais (IFs, Switches, etc.)<br />Polimorfismos<br />Loops<br />Operações<br />Etc..<br />Princípios<br />Página 12<br />
  13. 13. Exemplo de “Lista de Testes” para o caso de uso “Realizar Transferência Bancária”:<br />Realizar uma transferência normal (bem sucedida);<br />Tentar transferir uma quantidade superior ao saldo da conta de origem;<br />Verificar atomicidade no caso de falha de sistema antes de concluir a operação;<br />Conta de origem inativa;<br />Conta de destino inativa;<br />Valor transferido menor que zero;<br />Princípios<br />Página 13<br />
  14. 14. Exemplo de “Lista de Testes” para o caso de uso “Matricular Aluno em Disciplina”:<br />Matricular com sucesso;<br />Tentar matricular sem atender disciplinas pré-requisito;<br />Tentar matricular sem atender pré-requisito de crédito;<br />Tentar matricular com conflito de horário;<br />Tentar matricular sem vaga;<br />Tentar matricular em disciplina já cumprida;<br />Tentar matricular em disciplina que já está matriculada (outra turma);<br />Tentar matricular em disciplina que já está matriculada (mesma turma);<br />Princípios<br />Página 14<br />
  15. 15. 3. Primeiro o Teste<br />Oportunidade para pensar no design (projeto) das classes<br />Controlar o escopo do que será implementado – somente o necessário para atender o teste corrente.<br />4. Primeiro a Assertiva<br />É melhor pensarmos no que significa o sucesso do caso de teste antes de pensarmos no restante:<br />Vou precisar de criar um novo método?<br />Em qual classe?<br />Qual será o nome dele e seus parâmetros?<br />“Primeiro a assertiva” está para o caso de teste assim como “Primeiro o teste” está para o caso de uso;<br />Princípios<br />Página 15<br />
  16. 16. Exemplo: Testar o envio da string “Qualidata” através de um socket.<br />Princípios<br />Página 16<br />publicvoidTestarComunicacaoSocket()<br /> {<br />Assert.IsTrue(readerSocket.Close());<br />Assert.AreEqual(“Qualidata”, buf);<br /> }<br />publicvoidTestarComunicacaoSocket()<br /> {<br /> string buf = reader.Contents();<br />Assert.IsTrue(readerSocket.Close());<br />Assert.AreEqual(“Qualidata”, buf);<br /> }<br />publicvoidTestarComunicacaoSocket()<br /> {<br />SocketreaderSocket = newSocket(“localhost”, 8080);<br /> string buf = reader.Contents();<br />Assert.IsTrue(readerSocket.Close());<br />Assert.AreEqual(“Qualidata”, buf);<br /> }<br />publicvoidTestarComunicacaoSocket()<br /> {<br />Serverserver = newServer(8080, “”Qualidata”);<br />SocketreaderSocket = newSocket(“localhost”, 8080);<br /> string buf = reader.Contents();<br />Assert.IsTrue(readerSocket.Close());<br />Assert.AreEqual(“Qualidata”, buf);<br /> }<br />
  17. 17. 5. Dados para Teste<br />Não escolha números mágicos se eles não tiverem um significado específico no teste. Por exemplo, se não faz diferença utilizar “1”, “2” ou “1365”, preferia “1”. <br />Evite passar o mesmo valor para diferentes parâmetros. Por exemplo, para testar um método Operacao(int x, int y), não utilize Operacao(2,2), pois “Operacao” pode inverter “x” e “y” e o teste ainda assim passar. Prefira, por exemplo, Operacao(2,3).<br />Dê preferência a dados do mundo real, especialmente quando você tem algum sistema legado com dados que podem ser aproveitados<br />Princípios<br />Página 17<br />
  18. 18. 6. Dados com Significado Evidente<br />Lembre que está escrevendo um teste para alguém ler, e não somente para ser executado pelo computador.<br />Tente escrever na assertiva expressões que não só representem o valor final esperado, mas o que eles significam.<br />Exemplo:<br />Princípios<br />Página 18<br /> [Test]<br />publicvoid Testar_Fatorial_8()<br /> {<br />Assert.AreEqual(40320, Matematica.Fatorial(8));<br /> }<br /> [Test]<br />publicvoid Testar_Fatorial_8()<br /> {<br />Assert.AreEqual(8 * 7 * 6 * 5 * 4 * 3 * 2, Matematica.Fatorial(8));<br /> }<br />
  19. 19. Trata sobre quando escrever, onde escrever e quando parar de escrever testes.<br />Qual o próximo teste da lista a implementar?<br />Escolha um teste que você esteja confiante que pode implementá-lo facilmente;<br />Não comece pelo teste mais realístico (completo), pois você será forçado a implementar um monte de coisas para atender o teste.<br />Siga na direção “mais conhecidos” para “pouco conhecidos”<br />Cada iteração “red/green/refactor” deve levar apenas alguns minutos.<br />Exercício: Se você estiver construindo uma lista encadeada, qual seria seu primeiro caso de teste?<br />“Red Bar Patterns”<br />Página 19<br />
  20. 20. Testes de Estudo<br />Devemos escrever testes para componentes ou bibliotecas de terceiros que, supostamente, funcionam?<br />Sim. Para estudar e documentar seu uso.<br />Esse é uma forma de assegurar-se sobre como utilizar tal biblioteca de forma a obter os resultados esperados e documentar seu uso (considerando especificamente suas necessidades, e não todos os recursos da biblioteca).<br />“Red Bar Patterns”<br />Página 20<br />
  21. 21. Testes de Outras Funcionalidades<br />Sempre que uma discussão desviar do assunto principal e entrar em outras questões, essa pode ser uma hora de adicionar novos casos de teste à sua lista (para a funcionalidade em questão) e voltar para o assunto principal.<br />Sim. Para estudar e documentar seu uso.<br />Esse é uma forma de assegurar-se sobre como utilizar tal biblioteca de forma a obter os resultados esperados e documentar seu uso (considerando especificamente suas necessidades, e não todos os recursos da biblioteca).<br />“Red Bar Patterns”<br />Página 21<br />
  22. 22. Defeitos Reportados<br />Sempre que um defeito é reportado nossa primeira ação deve ser escrever um caso de teste que reproduza o problema. Se isso for feito certamente o problema será resolvido.<br />Testes de Regressão<br />Consiste em testar novamente uma funcionalidade que foi implementada e testada anteriormente;<br />Teoricamente, após qualquer alteração no sistema, todo o sistema deveria ser testado para garantir que essa alteração não tenha introduzido algum efeito colateral indesejável.<br />Uma boa prática é executar testes de regressão de todo o sistema, todos os dias, à noite, e reportar os problemas encontrados para a equipe. Existem softwares para gerenciar isso.<br />“Red Bar Patterns”<br />Página 22<br />
  23. 23. Técnicas mais detalhadas sobre como escrever testes.<br />Subdividir Testes<br />Quando um teste ficou grande demais e você está demorando muito para conseguir implementá-lo (mais de 10 min já deve te encomodar), pode ser melhor dividir o teste em testes menores até conseguir fazer o teste maior passar.<br />MockObjects<br />Como testar objetos que dependem de recursos externos custosos ou complexos, como bancos de dados e webservices, por exemplo?<br />Crie uma versão falsa ou simulada do objeto (“fake”) que retorne valores constantes.<br />Referência: www.mockobjects.com<br />Ferramentas para .NET: NMock e RhinoMocks.<br />“TestingPatterns”<br />Página 23<br />
  24. 24. Log String<br />Como testar se uma sequência de métodos foi chamada numa certa ordem?<br />Guarde um log (trace log) de cada execução dos métodos chamados em uma string e compare com o valor esperado. <br />Teste de Falhas<br />Precisamos assumir que “código que não foi testado não funciona”. Mas como testar falhas que são difíceis de se reproduzir (disco cheio, falha de comunicação, etc.)?<br />“Fake it!”, ou seja, construa uma nova classe que gere a exceção e simule a falha.<br />“TestingPatterns”<br />Página 24<br />
  25. 25. Desenvolvendo Sozinho – Teste Quebrado<br />Se você está desenvolvendo uma software sozinho, é sempre melhor encerrar uma sessão de programação deixando um teste vermelho(incompleto). <br />Isso agiliza a continuação do trabalho no próximo dia.<br />Desenvolvimento em Equipe – Testes Rodando<br />Se você trabalha em equipe, não faça check-in de testes vermelhos. Isso vai ser ruim pois todos vão achar que há um requisito que foi “estragado”, enquanto na verdade ele ainda não foi finalizado.<br />Ao subir suas alterações (check-in), rode todos os testes para garantir a integração entre os módulos.<br />Se os testes não forem muito lentos, procure rodar todos os testes em sua própria estação para antecipar problemas de integração.<br />“TestingPatterns”<br />Página 25<br />
  26. 26. Uma vez que você tenha um teste vermelho, busque torná-lo verde o quanto antes.<br />Use os padrões que serão discutidos para conseguir isso rapidamente, mesmo que o resultado seja algo que você não aceita conviver nem por uma hora.<br />“Green Bar Patterns”<br />Página 26<br />
  27. 27. “Fake it ‘til youmake it” (simule até construir realmente)<br />Por exemplo, qual é a forma mais simples de implementar o código abaixo?<br />A forma mas direta seria:<br />“Green Bar Patterns”<br />Página 27<br />publicvoidTestarSoma() {<br />Assert.AreEqual(5, Somar(2,3));<br /> }<br />publicvoidTestarSoma() {<br />Assert.AreEqual(5, Somar(2,3));<br /> }<br />publicint Somar(int x, int y) {<br />return 5;<br /> }<br />
  28. 28. Triangulação<br />Quando você tem dois ou mais testes, você precisa de uma implementação que atenda a todos simultaneamente.<br />Agora a solução trivial (constante) não atende mais, e somos forçados e implementar algo mais abrangente.<br />É claro que esse exemplo é apenas ilustrativo. Se você já tem uma implementação correta óbvia, então implemente. Senão, “fake it”.<br />“Green Bar Patterns”<br />Página 28<br />publicvoidTestarSoma() <br /> {<br />Assert.AreEqual(5, Somar(2,3));<br />Assert.AreEqual(7, Somar(3,4));<br /> }<br />
  29. 29. Implementação Óbvia<br />Se você consegue implementar algo diretamente, então implemente. Para que utilizar “fake” ou triangulação?<br />Mas quando você achar que sabe implementar e se deparar com uma barra vermelha? Depois você descobre, errei aqui, e aí outra barra vermelha! Talvez seja hora de dividir o problema em problemas menores.<br />Acreditar que você sempre vai conseguir acertar e escrever o código mais simples para o problema diretamente é exigir de você perfeição.<br />“TestingPatterns”<br />Página 29<br />
  30. 30. Operações Sobre Coleções<br />Implemente primeiro o teste para a operação recebendo um elemento;<br />Depois estenda para a operação recebendo a coleção;<br />Testes de Exceções<br />Como testar se em determinadas condições uma exceção é gerada?<br />Use try ... Teste ... Assert.IsTrue(false) ... catch ...<br />Ou use algum recurso específico do seu framework de testes (um “xUnit” da vida), se houver:<br />“TestingPatterns”<br />Página 30<br /> [ExpectedException(typeof(QException), "O atributo Pessoa.Nome não pode ser nulo.")]<br />publicvoidTestarNomeNaoNulo() <br /> {<br /> Pessoa p = new Pessoa();<br />Conexao.Save(p);<br /> }<br />
  31. 31. É muito importante pensar em “design patterns” ao projetar seu software;<br />Em TDD, “projeto” (design) não é exatamente uma fase, mas parte do processo “vermelho-verde-refatorar”. Nesse sentido eles recebem um olhar um pouco diferente em TDD.<br />Apenas para ilustrar, vamos destacar um “design patterns” bem interessante.<br />“Design Patterns”<br />Página 31<br />
  32. 32. Objeto NULL<br />Quantas vezes você já viu uma “nullreference exception”?<br />Quantas vezes você precisou proteger o seu código com um “if (obj != null) ...” ?<br />Ao invés de retornar “null”, pode ser melhor retornar um objeto especial, com a mesma interface, mas que não gere problemas. Exemplo (java.io.File):<br />“Design Patterns”<br />Página 32<br />publicbooleansetReadOnly() {<br />SecurityManagerguard = System.getSecurityManager();<br />if (guard != null) {<br />guard.canWrite(path);<br /> }<br />returnfileSystem.setReadOnly(this);<br />}<br />publicstaticSecurityManagergetSecurityManager() {<br />returnsecurity; //pode ser null<br />}<br />publicbooleansetReadOnly() {<br />SecurityManagerguard = System.getSecurityManager();<br />guard.canWrite(path);<br />returnfileSystem.setReadOnly(this);<br />}<br />publicstaticSecurityManagergetSecurityManager() {<br />returnsecurity == null ? newSecurityLiberadoGeral() : security;<br />}<br />
  33. 33. Refatorar é melhorar o “design” de funcionalidades do sistema, sem alterar seu comportamento;<br />Em TDD, devemos refatorar mantendo todos os testes “verdes”;<br />Em TDD, primeiro pensamos em resolver o problema da<br />Por que refatorar o código?<br />Para torná-lo mais legível;<br />Para facilitar sua manutenção, seja na correção de bugs ou na adição de novas funcionalidades, aumentado assim sua vida útil;<br />Para ajustar decisões de projeto estejam impactando negativamente o sistema (limitando, deixando lento, etc..)<br />Para buscar o “clean” de “cleancodethat works”<br />Embora seja um assunto que possa ser trabalhado de forma mais específica, trabalharemos alguns exemplos de técnicas de refatoração de código.<br />“Refactoring”<br />Página 33<br />
  34. 34. Reconcilie Diferenças<br />Se duas partes do código poderem ser iguais, então poderemos eliminar uma delas;<br />Dois loops poderiam ser fundidos em um só;<br />Se dois caminhos em um condicional forem iguais, poderemos eliminar a condição;<br />Se dois métodos foram iguais, podemos eliminar um;<br />Se duas classes forem iguais, podemos eliminar uma;<br />“Refactoring”<br />Página 34<br />
  35. 35. Isole as Alterações<br />Se você precisa alterar uma parte específica de um método divido em partes (seções), primeiro isole essa parte (refatoração) e depois faça a alteração;<br />Migrar Dados<br />Como migrar dados de uma estrutura para outra? (Por exemplo, de um DataSet para uma lista)<br />Duplique os dados primeiro criando uma nova variável (mantenha o DataSet e crie também a lista)<br />Inicialize também a nova variável (lista) em todo lugar que a antiga (DataSet) é inicializada;<br />Substitua a implementação para que seja utilizada a nova estrutura variável (lista) ao invés da antiga (DataSet)<br />Elimine a variável da estrutura original (o DataSet).<br />Entre todos esses passos os testes não devem deixar de permanecer verde.<br />“Refactoring”<br />Página 35<br />
  36. 36. Extraia Métodos<br />Simplifique métodos muito longos, criando métodos menores (variáveis locais utilizadas também fora do trecho devem virar parâmetros)<br />Não se deve ir tão longe ao ponto de criar tantas indireções que acabem dificultando a leitura.<br />Blocos de código com aquele típico comentário no início são excelentes candidatos a novos métodos. Exemplo:<br />“Refactoring”<br />Página 36<br /> //Verifica se o aluno atende a todos os pré-requisitos para ser matriculado<br /> linha 1<br /> linha 2<br /> ...<br /> linha N<br />
  37. 37. Métodos “Inline”<br />Esse é o ato de substituir uma chamada de um método pelo seu conteúdo (o oposto da técnica anterior)<br />Isso é especialmente útil quando você está tendo dificuldade de entender um código confuso, cheio de indireções. <br />Nesses casos pode ser melhor simplesmente desfazer (via “inline”) algumas camadas de abstração, entender o que o código faz, e depois repensar a melhor forma de reorganizá-lo.<br />“Refactoring”<br />Página 37<br />
  38. 38. Mover Métodos<br />Como mover métodos para outros “lugares” sem exigir uma alteração imediata em todos as suas chamadas?<br />Crie o novo método no lugar que deseja;<br />Mova o conteúdo do método;<br />No método original, faça uma chamada ao novo método, repassando os parâmetros.<br />Adicionar Novo Parâmetro<br />Pode ser visto como um caso de “Migrar Dados”<br />Também pode ser utilizado o próprio compilador para mostrar onde será necessário alterar sua chamada;<br />“Refactoring”<br />Página 38<br />
  39. 39. Mover Parametros de Métodos para Construtores<br />Por que? Se você passa o mesmo parâmetro para vários métodos de uma classe, pode ser melhor passá-lo uma única vez em seu constructor, e simplificar as chamadas dos métodos;<br />Como?<br />Adicione o parâmetro ao constructor;<br />Adicione uma nova variável de instância com o mesmo nome do parâmetro, e inicialize ela no constructor;<br />Um por um, converta todas as ocorrências de “parametro” nos métodos por “this.parametro”.<br />“Refactoring”<br />Página 39<br />
  40. 40. Mover Parametros de Métodos para Construtores (...continuação)<br />Quando não houver mais referências a “parametro”, elimine-o da assinatura do método e de todas suas chamadas;<br />Remova o “this.” que agora é supérfluo;<br />Renomeie a variável de instância apropriadamente.<br />“Refactoring”<br />Página 40<br />
  41. 41. Qual o tamanho ideal dos passos “vermelho-verde-refatorar”?<br />Você pode escrever testes que vão te levar a escrever uma única linha de código;<br />Também pode escrever testes que vão te levar à escrever centenas de linha de código;<br />Não há uma regra, mas a tendência dos experientes em TDD é clara: Passos pequenos.<br />“Mastering TDD”<br />Página 41<br />
  42. 42. Características de um bom teste<br />Se você tem dificuldade para identificar um lugar comum para inicializações compartilhadas, então você deve ter um problema de projeto – muitos objetos entrelaçados.<br />Quanto feedback (asserts) você precisa?<br />Depende. Por exemplo, pode ser muito improvável um problema de diskfull, e você está confiante que esse é um teste desnecessário no cenário do software em questão.<br />“Mastering TDD”<br />Página 42<br />
  43. 43. Quando excluir um teste?<br />Quanto mais testes, melhor;<br />Se dois testes fazem a mesma coisa, pode ser, mas considere:<br />Na dúvida, não exclua;<br />Se os dois testes seguem o mesmo fluxo de execução, mas comunicam (documentam) questões diferentes, deixe-os assim.<br />TDD é Escalável?<br />O maior sistema totalmente TDD que o autor participou foi na LifeWare:<br />40 pessoas, por 4 anos<br />250KLOC de código funcional<br />250KLOC de código de teste<br />4000 testes que levam 20min para executar<br />“Mastering TDD”<br />Página 43<br />
  44. 44. Como iniciar TDD em um sistema desenvolvido sem Testes Automatizados?<br />Muitos livros ainda precisam ser escritos sobre isso;<br />Um primeiro problema é que código que não foi escrito pensando nos testes é difícil de ser testado;<br />Outro problema é que alterações nesse código são perigosas pois não temos os testes para garantir que tudo continua funcionando;<br />O ideal seria primeiramente implementar testes automáticos para todo o sistema e depois partir para desenvolver as novas funcionalidades com TDD;<br />Outra opção é explorar outras formas de feedback, como testes de nível de aplicação, que garanta certo nível de confiança.<br />“Mastering TDD”<br />Página 44<br />
  45. 45. A Metodologia TDD<br />Estudo de Caso: Aplicação do TDD na IBM<br />Tese de Doutorado - MirkeyaCapellán (2006)<br />Measuring Productivity and Code Quality: Test-Driven Development vs. Waterfall<br />Construção de Casos de Testes com NUnit<br />Considerações Finais<br />Organização da Apresentação<br />Página 45<br />
  46. 46. Baseado no artigo: <br /> [1] E. M. Maximilien and L. Williams, "Assessing test-driven development at IBM", Proceedings of the 25th International Conference on Software Engineering pp. 564-569, Portland, Oregon, USA, IEEE Computer Society, 2003.<br />O sistema: JavaPOS (www.javapos.com) <br />Um conjunto de serviços (JavaBeans) para permitir o acesso a dispositivos POS (pointofsale), tais como impressoras, caixas (dinheiro), leitores magnéticos, etc..<br />Implementado através de uma parceria da IBM, NCR, Epson e Sun.<br />TDD na IBM<br />Página 46<br />
  47. 47. Motivação<br />Implementação do JavaPOS original (sem TDD) contava com uma equipe que conehcia muito bem suas especificações e tinha grande experiência com dispositivos POS;<br />Em cada fase de FVT (Functional Verification Test), executada antes de cada novo release, a taxa de defeitosnãodiminuíacomoesperado.<br />Porissodesenvolvedores e gerentesestavamabertos a novas abordagens.<br />TDD na IBM<br />Página 47<br />
  48. 48. Experiência Anterior com Testes<br />Criavam algumas classes de teste de forma ad-hoc para partes mais importantes do código;<br />O processo de construção de testes não era sistemático, sendo uma atividade pós-desenvolvimento;<br />Na maioria das vezes, nenhum teste era criado. Especialmente quando os cronogramas estavam apertados, os desenvolvedores precisavam parar para resolver problemas de desenvolvimentos anteriores, ou ainda os novos requisitos não estavam muito claros<br />TDD na IBM<br />Página 48<br />
  49. 49. Experiência Anterior com Testes (cont.)<br />A maioria dos testes de unidade eram desperdiçados;<br />Eles não eram executados na fase de verificação funcional (FVT)<br />Também não eram aproveitados em versões posteriores (novos releases) do software;<br />Não tinham qualquer experiência com TDD.<br />TDD na IBM<br />Página 49<br />
  50. 50. Adoção da Metodologia TDD - No início havia muitas dúvidas:<br />Taxa de Defeitos – Como TDD vai afetar a taxa de defeitos a curto prazo (próximo release) e a longo prazo (futuros releases)?<br />Produtividade - Qual será o impacto? Trabalhavam com uma média de 400LOC / pessoa-mês.<br />Frequência dos Testes – Qual será o percentual de testes automatizados em relação a testes iterativos? Quão frequentemente cada tipo de teste será executado?<br />Projeto – Como TDD vai afetar a qualidade do projeto (design)? Eles avaliam isso através da capacidade de lidar com requisitos identificados tardiamente, ou a inclusão de novos dispositivos ou serviços.<br />Integração – TDD e seus testes de regressão automatizados irão suavizar os processos de integração?<br />TDD na IBM<br />Página 50<br />
  51. 51. O Trabalho com TDD<br />No início muitos desenvolvedores e gerentes estavam preocupados pois uma metodologia tão rigorosa poderia afetar a produtividade a ponto de inviabilizar a manutenção dos cronogramas existentes.<br />Apesar das metodologias ágeis sugerirem nenhum projeto a priori, no javaPOS os requisitos eram bem estáveis, e a equipe optou por desenvolver diagramas de classes e de sequência;<br />Acrescentaram nas atividades de projeto, além dos diagramas UML, a criação de casos de teste (teste de sistema);<br />TDD na IBM<br />Página 51<br />
  52. 52. O Trabalho com TDD<br />Para cada classe pública A, tinham uma classe TesteA, e para cada método de A, métodos de teste correspondentes em TesteA.<br />Havia casos em que era necessário tratar cenários diferentes do mesmo caso de teste. Por exemplo, impressoras que trabalhavam de modo síncrono ou assíncrono, exigiam casos de teste específicos para cada cenário;<br />Definiram como meta um mínimo de 80% de cobertura de teste automatizado (o restante poderia ser testes iterativos);<br />TDD na IBM<br />Página 52<br />
  53. 53. O Trabalho com TDD<br />Cada documento de especificação de projeto continha uma secção de testes descrevendo todas as classes e métodos importantes que deveriam ser testados;<br />Diariamente todo código era compilado automaticamente, e todos os testes executados.<br />Para cada build, um email era enviado para cada membro da equipe com a lista de testes executados e eventuais erros encontrados.<br />Em um primeiro momento, esses builds eram gerados várias vezes por dia;<br />TDD na IBM<br />Página 53<br />
  54. 54. TDD na IBM<br />Página 54<br />
  55. 55. Resultados<br />Números do Projeto:<br />71.4 KLOC de código de negócio e 34 KLOC de código Junit;<br />Densidade de erros, que normalmente é de 8 erros/KLOC foi de 4 erros/KLOC (50% de redução)<br />OBS: Atualmente, devido ao TDD, nossa densidade média de erros é de 3.7 erros/KLOC.<br />Foram escritos 2390 casos de teste JUnit. Outros 100 testes foram escritos para avaliar performance;<br />TDD na IBM<br />Página 55<br />
  56. 56. Resultados<br />Taxa de Defeitos – Com TDD, os testes automatizados realmente foram feitos. Como resultado obtiveram um excelente redução de 50% de defeitos na fase de verificação funcional (FVT)<br />Produtividade – Não só manteve-se dentro dos cronogramas, como obtiveram uma pequena melhoria em relação à média de 400LOC/pessoa-mês;<br />Frequência dos Testes – Escreveram aproximadamente 2500 testes automáticos e 400 testes iterativos, ou seja, 86% de cobertura de teste automático, acima da meta de 80%.<br />Projeto – Acreditam que TDD ajudou a produzir um sistema que recebe mais facilmente mudanças. Inclusive alguns dispositivos foram incluídos no projeto quando o mesmo já tinha avançado mais de 2/3 do cronograma.<br />Integração – Antes integravam somente próxima à fase de FVT. Com TDD e integrações (builds) diários, diminuíram muito os riscos pois problemas aparecem bem mais cedo.<br />TDD na IBM<br />Página 56<br />
  57. 57. Lições aprendidas sobre a transição para TDD:<br />Comece TDD no início do projeto;<br />Ajuste as expectativas da equipe pois no início TDD parece menos produtivo e frustrante pelo tempo que se leva para criar os testes; Na prática, no final das contas, não haverá impacto negativo na produtividade.<br />Para uma equipe nova em TDD, inicie builds diários mais adiante um pouco. Não muito cedo nem muito tarde (após 1/3 do cronograma, por exemplo). Enquanto isso cada um deve executar os seus testes em sua estação.<br />TDD na IBM<br />Página 57<br />
  58. 58. Lições aprendidas sobre a transição para TDD:<br />Convença a equipe de adicionar novos casos de tese sempre que um problema for encontrado, não importando onde seja. Isso vai garantir a solução do problema em algum momento à frente;<br />Definimos 80% de cobertura como um objetivo mínimo. É extremamente importante os builds diários automáticos. Eles se tornam a batida que dá o ritmo ao projeto e ao mesmo tempo facilita acompanharmos seu progresso;<br />Encorage a construção de testes que executem rapidamente e de projeto eficiente;<br />Aproveite sempre para encorajar a equipe a escrever mais testes, por exemplo, monitorando qual subsistema tem mais testes implementados.<br />TDD na IBM<br />Página 58<br />
  59. 59. A Metodologia TDD<br />Estudo de Caso: Aplicação do TDD na IBM<br />Tese de Doutorado - MirkeyaCapellán (2006)<br />Measuring Productivity and Code Quality: Test-Driven Development vs. Waterfall<br />Construção de Casos de Testes com NUnit<br />Considerações Finais<br />Organização da Apresentação<br />Página 59<br />
  60. 60. Experimento<br />24 profissionais de 3 empresas trabalhando em pares. Metade com TDD e a outra metade seguindo um Modelo em Cascata. Apenas uma empresa tinha experiência prévia com TDD;<br />Resultados:<br />TDD produziu um código de mais qualidade do que o Modelo Cascata<br />18% a mais de sucesso em testes de caixa preta<br />80% mais eficaz em termos de qualidade de código<br />TDD teve um tempo total de desenvolvimento bem menor:<br />O de programação, especificamente, foi 16% maior;<br /> O projeto como um todo foi 78% mais podutivo;<br />Programadores acharam TDD uma forma mais simples de projetar software;<br />Transição para TDD foi difícil<br />TDD versus ModeloCascata<br />Página 60<br />
  61. 61. A Metodologia TDD<br />Estudo de Caso: Aplicação do TDD na IBM<br />Tese de Doutorado - MirkeyaCapellán (2006)<br />Measuring Productivity and Code Quality: Test-Driven Development vs. Waterfall<br />Construção de Casos de Testes com NUnit<br />Considerações Finais<br />Organização da Apresentação<br />Página 61<br />
  62. 62. A Metodologia TDD<br />Estudo de Caso: Aplicação do TDD na IBM<br />Tese de Doutorado - MirkeyaCapellán (2006)<br />Measuring Productivity and Code Quality: Test-Driven Development vs. Waterfall<br />Construção de Casos de Testes com NUnit<br />Considerações Finais<br />Organização da Apresentação<br />Página 62<br />

×