Test-Driven Development O que é?  O que não é?   E o que isto tem a ver com você? Carlos Eduardo Miranda Arquiteto .Net Add Technologies
Agenda Introdução Por que ela “ainda” não é um padrão amplamente utilizado pelo mercado? TDD - O que é? TDD - Benefícios TDD – O que não é? Mitos sobre o TDD Prática do TDD E caso algum teste tenha falhado? Encontrei um bug, e agora? Tudo Verde! Done! Dificuldades na implantação do TDD na Empresa O que isto tem a ver com você? Glossário Solução – Criação de uma classe de Pilha – Stack
Introdução O assunto nunca esteve tão em voga quanto nos últimos anos; Não se trata de mais apenas um “modismo“ do mercado; Traz diversos benefícios para as empresas e clientes; Maior parte dos projetos de software não atigem os seus objetivos em pelo menos uma das dimensões (tempo, custo, qualidade, escopo); Desenvolver software não é desenvolver um produto físico; Softwares apoiam as áreas de negócio das empresas e estes mercados mudam constantemente; Os requisitos vão mudar!
Por que ela “ainda” não é um padrão amplamente utilizado pelo mercado? Contra-Produtiva e Contra-Intuitiva à primeira vista (“Como assim criar um teste de uma classe que ainda não existe?!”); Desenvolvedores não gostam de criar testes unitários, pois são chatos; Falta de tempo e recursos (cronogramas apertados e poucos recursos); Resistência à mudanças; Desconhecimento por parte de Gerentes e Desenvolvedores; Desenvolvimento ágil ainda é visto com desconfiança e como “sem controle”;
TDD - O que é? Prática que foi originada nas rodas de desenvolvedores SmallTalk; Amplamente utilizada em metodologias ágeis como o XP (eXtreme Programming) e o Scrum; Prática para “Desenvolvimento de Software” que  prega que todo e qualquer desenvolvimento deva ser precedido da criação de uma suíte de testes e que toda duplicação de código deve ser eliminada; Testes são desenvolvidos na mesma linguagem de programação do sistema em desenvolvimento; Testes quando criados podem até mesmo não compilar, neste momento eles não necessitam compilar; A única regra é que os testes sejam criados antes da implementação das funcionalidades do sistema;
TDD - Benefícios Melhora na interface das classes da aplicação; Maior desacoplamento das classes ; Aumento na Produtividade; Aumento da confiança pelo desenvolvedor em fazer alterações; Possibilidade de fazer Refactor (Refatoração de código); Cobertura de cenários da aplicação; Possibilidade de auto documentação a partir da Suite de Testes; Flexibilidade – maior facilidade em abraçar às mudanças nos requisitos; Deploy simplificado – g arantia de uma versão estável a todo o momento, pronta para subir para a homologação ou produção ;
TDD – O que não é? Ferramenta de teste; A Resolução de todos os problemas no desenvolvimento de sistemas; Padrões de Projeto (Design Patterns);
Mitos sobre o TDD Test-Driven Development != QA Test-Driven Development != Testes Unitários Test-Driven Development != Design Patterns
Prática do TDD 0. Setup (somente executado uma vez no ciclo de vida do projeto); Red – Criar um teste que falhe (eliminar falsos positivos); Green – Criar a implementação mais simples possível que faça o teste passar; Refactor! – Refatorar a implementação, melhorando-a de modo incremental e controlada, fazendo com que o código comunique melhor o seu intento, eliminando duplicidades no código,  fazendo as alterações arquiteturais (se estas se mostrarem necess á rias) e e xecutando os testes a cada alteração por menor que esta seja;
E caso algum teste tenha falhado? Desfazer as últimas alterações, e refazer a funcionalidade de modo a não quebrar as funcionalidades construídas previamente; O ideal é que a aplicação não seja quebrada (testes em vermelho) por mais do que poucos minutos; Passos curtos, incrementais e controlados;
Encontrei um bug, e agora? Criar um teste que exponha este cenário descoberto, realizar a prática do TDD ( Red  –  Green  –  Refactor! ), assim como qualquer outro teste previamente planejado; No longo prazo com a utilização desta prática teremos um produto cada vez melhor com uma quantidade maior de cenários cobertos, menos retrabalho e um número menor de bugs;
Tudo Verde! Done! Após a criação da sua Suíte de testes e a implementação do código que faz com que todos estes testes passem (assim como os testes para resolução de  bugs ), ou seja, tudo está verde, podemos concluir que o trabalho foi terminado para a iteração corrente;
Dificuldades na implantação do TDD na Empresa Investimento em treinamento; Grande barreiras culturais na empresa; Quebra de paradigma muito forte, pois põem em cheque conceitos maduros na área de Desenvolvimento de Software (considerados como verdades absolutas);
O que isto tem a ver com você? Mais e mais empresas estão abraçando a idéia de metodologias de desenvolvimento ágeis como XP e SCRUM; Demanda por profissionais que conheçam e já se utilizem destas metodologias e pr á ticas deve crescer ao longo dos próximos anos; Desenvolvedores e empresas que abraçarem a mudança e assumirem que os requisitos vão mudar e que a maneira como estamos gerenciando os nossos projetos não é produtiva, certamente se beneficiarão mais e estarão na ponta quando o mercado como um todo fizer a transição; Eu estarei lá. E você?
Glossário TDD – Test-Driven Development – Desenvolvimento Dirigido a Testes. QA – Quality Assurance – Garantia da Qualidade.  XP – eXtreme Programming – Programação Extrema é uma metodologia ágil de desenvolvimento de software ágil, criada por Kent Beck. SCRUM – é uma metodologia de gerenciamento de projetos, cujo nome vem de uma jogada do esporte rugby, em que os jogadores de cada time se encaixam formando uma espécie de muralha e onde outro jogador joga a bola no meio do túnel formado para que os dois grupos a disputem. Assim como em todas as metodologias ágeis o trabalho em equipe é fundamental.
Solução – Criação de uma classe de Pilha – Stack Problema: Criar uma classe que represente uma pilha ilimitada em memória e que o acesso seja restrito ao último elemento inserido na pilha;
Criar a Test List para a Pilha Analisando o requisito passado (Stack), foram observados os seguintes testes iniciais: Criar uma pilha e verificar que está vazia; Inserir um objeto na Pilha e verificar que não está vazia; Inserir um objeto na Pilha , retirar este, e verificar que está vazia; Inserir um objeto na Pilha (guardando o seu valor), retirar este da Pilha e verificar que estes são iguais; Inserir 3 objetos em sequência (guardando seus valores), e removê-los verificando se estes são removidos na ordem correta; Retirar um elemento de uma Pilha sem elementos; Inserir um objeto na Pilha, buscar quem é o topo da Pilha e verificar que a Pilha não está vazia; Inserir um objeto na Pilha (guardando o seu valor), buscar quem é o topo da Pilha e verificar que estes são iguais; Buscar o topo de uma Pilha vazia;
Análise da TestList Analisando a TestList vemos que existem 3 operações e uma propriedade que podem ser executadas na Pilha: Push(Object)  (inserir um item na pilha); Pop()  (remover um item da pilha, retonando-o); Top()  (retornar o primeiro elemento sem retirá-lo da pilha); IsEmpty  (retorna um booleano indicando se a pilha está vazia);
Setup (executado somente uma vez) Baixar do site  http://www.nunit.org , o programa msi instalador; Executar o programa de instalação do Nunit; Criar um projeto de testes para a classe a ser criada  Stack ; Referenciar a “dll” do framework de testes Nunit ( Nunit.Framework.dll ) no projeto de testes; Referenciar o projeto da classe a ser testada; Fazer deste projeto, o projeto inicial da Solução; Configurar a inicialização automática do programa  Nunit.exe  (interface visual dos testes unitários) na compilação do Projeto de Testes;
Teste 1 O primeiro teste escolhido foi verificar se uma pilha nova está vazia: // StackTests .cs  using  System; using  NUnit.Framework; [ TestFixture ] public class  StackTests { [ Test ] public void  Empty() { Stack  stack =  new   Stack (); Assert .IsTrue(stack.IsEmpty); } } Executar a solução; Erros de compilação (Stack não existe);
Teste 1 ( Red ): Implementar o menor código que faça o teste compilar // Stack.cs using  System; public class  Stack { public   Boolean  IsEmpty {   get  { return  false ; } } } Executar a solução; Solução compilada com sucesso; Teste Empty falha (Red);
Teste 1 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; public class  Stack { public   Boolean  IsEmpty {   get  { return  true ; } } } Executar a solução; Solução compilada com sucesso; Teste Empty executado com sucesso;
Teste 1 ( Refactor! ): Melhorar a implementação // Stack.cs using  System; public class  Stack { public  Stack() {   this . _isEmpty  =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } } Executar a solução; Solução compilada com sucesso; Teste Empty continua executando com sucesso;
Teste 2 O segundo teste escolhido foi verificar se ao adicionar um elemento a pilha não está vazia: // StackTests .cs  [ Test ] public void  PushOne() { Stack  stack =  new   Stack (); stack.Push( “primeiro elemento” ); Assert .IsFalse(stack.IsEmpty,  “Após a inclusão, IsEmpty deve ser false.” ); } Executar a solução; Erros de compilação (Stack não possui o método Push(Object));
Teste 2 ( Red ): Implementar o menor código que faça o teste compilar // Stack.cs using  System; public class  Stack { public  Stack() {   this ._isEmpty =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } public   void   Push( Object  element_) { } } Executar a solução; Solução compilada com sucesso; Teste PushOne falha (Red);
Teste 2 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; public class  Stack { public  Stack() {   this ._isEmpty =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } public   void   Push( Object  element_) {   this ._isEmpty = false; } } Executar a solução; Solução compilada com sucesso; Teste PushOne executado com sucesso;
Teste 2 ( Refactor! ): Melhorar a implementação // StackTests .cs  using  System; using  NUnit.Framework; [ TestFixture ] public class  StackTests { private   Stack  stack; [ SetUp ] public void  Init() { stack =  new   Stack (); } [ Test ] public void  Empty() { Assert .IsTrue(stack.IsEmpty); } [ Test ] public void  PushOne() { stack.Push( “primeiro elemento” ); Assert .IsFalse(stack.IsEmpty,  “Após um Push, IsEmpty deve ser false.” ); } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 3 O terceiro teste escolhido foi verificar se ao adicionar um elemento e retirá-lo a pilha está vazia: // StackTests .cs  [ Test ] public void  PushAndPop() { stack.Push( “primeiro elemento” ); stack.Pop(); Assert .IsTrue(stack.IsEmpty,  “Após uma operação Push - Pop, IsEmpty deve ser true.” ); } Executar a solução; Erros de compilação (Stack não possui o método Pop());
Teste 3 ( Red ): Implementar o menor código que faça o teste compilar // Stack.cs using  System; public class  Stack { public  Stack() {   this . _isEmpty  =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this . _isEmpty ; } } public   void   Push( Object  element_) {   this . _isEmpty  =  false ;  } public   void   Pop() { } } Executar a solução; Solução compilada com sucesso; Teste PushAndPop falha (Red);
Teste 3 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; public class  Stack { public  Stack() {   this ._isEmpty =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } public   void   Push( Object  element_) {   this ._isEmpty =  false ;  } public   void   Pop() {   this ._isEmpty =  true ;  } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 3 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 4 O quarto teste escolhido foi adicionar um elemento (lembrando o seu valor), retirá-lo e verificar se o elemento é o mesmo: // StackTests .cs  [ Test ] public void  PushAndPopContentCheck() { Int32   expected = 1234; stack.Push(expected);   Int32   actual = ( Int32 )stack.Pop(); Assert .AreEqual(expected , actual); } Executar a solução; Erros de compilação (o método Pop() retorna void);
Teste 4 ( Red ): Implementar o menor código que faça o teste compilar // Stack.cs using  System; public class  Stack { public  Stack() {   this ._isEmpty =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } public   void   Push( Object  element_) {   this ._isEmpty =  false ;  } public   Object  Pop() {   this . _isEmpty =  true ;  return null ; } } Executar a solução; Solução compilada com sucesso; Teste PushAndPopContentCheck falha (Red);
Teste 4 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; public class  Stack { private  Object  _element; public  Stack() {   this ._isEmpty =  true ;  } private  Boolean  _isEmpty; public   Boolean  IsEmpty {   get  { return  this ._isEmpty; } } public   void   Push( Object  element_) {   this ._element = element_;    this ._isEmpty =  false ;  } public   Object  Pop() {   this ._isEmpty =  true ;  Object   top =  this ._element;   this ._element =  null ;   return  top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 4 ( Refactor! ): Melhorar a implementação // Stack.cs using  System; public class  Stack { private  Object  _element; public  Stack() { } public   Boolean  IsEmpty {   get  { return  this ._element ==  null ; } } public   void   Push( Object  element_) {   this ._element = element_;  } public   Object  Pop() { Object   top =  this ._element ;   this ._element =  null ;   return  top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 5 ( Red ) O quinto teste escolhido foi adicionar 3 elementos (lembrando os seus valores), retirá-los e verificar se os elementos são os mesmos: // StackTests .cs  [ Test ] public void  PushAndPopMultipleContentCheck() { String  pushed1 =  “1” ; stack.Push(pushed1); String  pushed2 =  “2” ; stack.Push(pushed2); String  pushed3 =  “3” ; stack.Push(pushed3);   String  popped = stack.Pop()  as   String ; Assert .AreEqual(pushed3, popped); popped = stack.Pop()  as   String ; Assert .AreEqual(pushed2, popped); popped = stack.Pop()  as   String ; Assert .AreEqual(pushed1, popped); } Executar a solução; Solução compilada com sucesso; Teste PushAndPopMultipleContentCheck falha (Red);
Teste 5 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; using  System.Collections; public class  Stack { private  ArrayList  _elements = new  ArrayList (); public   Boolean  IsEmpty {   get  { return  this ._elements.Count == 0; } } public   void   Push( Object  element_) {   this ._elements.Insert(0, element_);  } public   Object  Pop() { Object   top =  this ._elements[0];   this ._elements.RemoveAt(0);   return  top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 5 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 6 ( Red ) O sexto teste escolhido foi tentar retirar um elemento de uma pilha sem elementos: // StackTests .cs  [ ExpectedException ( typeof ( InvalidOperationException ))] [ Test ] public void  PopEmptyStack() { stack.Pop(); } Executar a solução; Solução compilada com sucesso; Teste PopEmptyStack falha (Red);
Teste 6 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; using  System.Collections; public class  Stack { private  ArrayList  _elements = new  ArrayList  (); public   Boolean  IsEmpty {   get  { return  this ._elements.Count == 0; } } public   void   Push( Object  element_) {   this ._elements.Insert(0, element_);  } public   Object  Pop() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); Object   top =  this ._elements[0];   this ._elements.RemoveAt(0);   return  top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 6 ( Refactor! ): Melhorar a implementação Ao realizar este teste vieram à tona mais alguns testes que deveriam ser incluídos em nossa TestList: Inserir nulo em uma pilha e verificar que ela não está vazia. Inserir nulo em uma Pilha, retirá-lo e verificar que o valor retornado é nulo. Inserir nulo em uma Pilha, chamar Top e verificar que o valor retornado é nulo.
Teste 7 O sétimo teste escolhido foi incluir um único objeto,  chamar Top e verificar se a lista não está vazia: // StackTests .cs  [ Test ] public void  PushTop() { stack.Push(“42”);   stack.Top();   Assert .IsFalse(stack.IsEmpty); } Executar a solução; Erros de compilação (o método Top() não existe);
Teste 7 ( Green ): Implementar o menor código que faça o teste compilar // Stack.cs using  System; using  System.Collections; public class  Stack { private  ArrayList  _elements = new  ArrayList (); public   Boolean  IsEmpty {   get  { return  this ._elements.Count == 0; } } public   void   Push( Object  element_) {   this ._elements.Insert(0, element_);  } public   Object  Pop() { if ( this . IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); Object   top =  this ._elements[0];   this ._elements.RemoveAt(0);   return  top; } public   Object  Top() {   return  null ;  } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 7 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 8 ( Red ) O oitavo teste escolhido foi inserir um elemento na Pilha (guardando o seu valor), chamar Top e verificar que os elementos são iguais: // StackTests .cs  [ Test ] public void  PushTopContentCheckElement() { String  pushed =  “42” ;   stack.Push(pushed);   String  topped = stack.Top()  as   String ;   Assert .AreEqual(pushed, topped); } Executar a solução; Solução compilada com sucesso; Teste PushTopContentCheckElement falha (Red);
Teste 8 ( Green ): Fazer o teste passar com a mais simples implementação using  System; using  System.Collections; public class  Stack { private  ArrayList  _elements = new  ArrayList (); public   Boolean  IsEmpty {   get  { return  this ._elements.Count == 0; } } public   void   Push( Object  element_) {   this ._elements.Insert(0, element_);  } public   Object  Pop() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); Object   top =  this ._elements[0];   this ._elements.RemoveAt(0);   return  top; } public   Object  Top() {   return this ._elements[0];  } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 8 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 9 ( Green ) O nono teste escolhido foi inserir múltiplos elementos na Pilha (guardando o valor do último inserido), chamar Top e verificar que os elementos são iguais: // StackTests .cs  [ Test ] public void  PushMultipleTopContentCheckElement() { String  pushed1 =  “1” ; stack.Push(pushed1); String  pushed2 =  “2” ; stack.Push(pushed2); String  pushed3 =  “3” ; stack.Push(pushed3);   String  topped = stack.Top()  as   String ; Assert .AreEqual(pushed3, topped); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 9 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 10 ( Green ) O décimo teste chamar Top diversas vezes e verificar que os elementos são iguais: // StackTests .cs  [ Test ] public void  MultipleTopContentCheckElement() { String  pushed =  “1”; stack.Push(pushed);   String  topped1 = stack.Top()  as   String ;   Assert .AreEqual(pushed , topped1);   String  topped2 = stack.Top()  as   String ;   Assert .AreEqual(topped1, topped2);   String  topped3 = stack.Top()  as   String ; Assert .AreEqual(topped2, topped3); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 10 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 11 ( Red ) O décimo primeiro teste é chamar Top em uma pilha vazia // StackTests .cs  [ ExpectedException ( typeof ( InvalidOperationException ))] [ Test ] public void  TopEmpty() { stack.Top(); } Executar a solução; Solução compilada com sucesso; Teste TopEmpty falha (Red);
Teste 11 ( Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using  System; using  System.Collections; public class  Stack { … public   Object  Pop() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); Object   top =  this ._elements[0];   this ._elements.RemoveAt(0);   return  top; } public   Object  Top() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” );   return this ._elements[0];  } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 11 ( Refactor! ): Melhorar a implementação … public   Object  Pop() { Object   top = Top(); this ._elements.RemoveAt(0); return  top; } public   Object  Top() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); return this ._elements[0];  } … Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 12 ( Green ) O décimo segundo teste é inserir nulo e verificar que a pilha não está vazia. // StackTests .cs  [ Test ] public void  PushNull () { stack.Push( null ); Assert .IsFalse(stack.IsEmpty); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 12 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 13 ( Green ) O décimo terceiro teste é inserir um objeto nulo chamar Pop e verificar que o valor retornado é nulo e que a pilha está vazia. // StackTests .cs  [ Test ] public void  PushNullCheckPop() { stack.Push( null );   Assert .IsNull(stack.Pop()); Assert .IsTrue(stack.IsEmpty); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 13 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Teste 14 ( Green ) O décimo quarto teste é inserir um objeto nulo chamar Top e verificar que o valor retornado é nulo. // StackTests .cs  [ Test ] public void  PushNullCheckTop() { stack.Push( null );   Assert .IsNull(stack.Top()); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
Teste 14 ( Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
Stack.cs using  System; using  System.Collections; public class  Stack { private  ArrayList  _elements = new  ArrayList (); public   Boolean  IsEmpty {   get  { return  this ._elements.Count == 0; } } public   void   Push( Object  element_) {   this ._elements.Insert(0, element_);  } public   Object  Pop() { Object   top = Top(); this ._elements.RemoveAt(0); return  top; } public   Object  Top() { if ( this .IsEmpty) throw new  InvalidOperationException ( “A pilha está vazia.” ); return this ._elements[0];  } }

Test driven development

  • 1.
    Test-Driven Development Oque é? O que não é? E o que isto tem a ver com você? Carlos Eduardo Miranda Arquiteto .Net Add Technologies
  • 2.
    Agenda Introdução Porque ela “ainda” não é um padrão amplamente utilizado pelo mercado? TDD - O que é? TDD - Benefícios TDD – O que não é? Mitos sobre o TDD Prática do TDD E caso algum teste tenha falhado? Encontrei um bug, e agora? Tudo Verde! Done! Dificuldades na implantação do TDD na Empresa O que isto tem a ver com você? Glossário Solução – Criação de uma classe de Pilha – Stack
  • 3.
    Introdução O assuntonunca esteve tão em voga quanto nos últimos anos; Não se trata de mais apenas um “modismo“ do mercado; Traz diversos benefícios para as empresas e clientes; Maior parte dos projetos de software não atigem os seus objetivos em pelo menos uma das dimensões (tempo, custo, qualidade, escopo); Desenvolver software não é desenvolver um produto físico; Softwares apoiam as áreas de negócio das empresas e estes mercados mudam constantemente; Os requisitos vão mudar!
  • 4.
    Por que ela“ainda” não é um padrão amplamente utilizado pelo mercado? Contra-Produtiva e Contra-Intuitiva à primeira vista (“Como assim criar um teste de uma classe que ainda não existe?!”); Desenvolvedores não gostam de criar testes unitários, pois são chatos; Falta de tempo e recursos (cronogramas apertados e poucos recursos); Resistência à mudanças; Desconhecimento por parte de Gerentes e Desenvolvedores; Desenvolvimento ágil ainda é visto com desconfiança e como “sem controle”;
  • 5.
    TDD - Oque é? Prática que foi originada nas rodas de desenvolvedores SmallTalk; Amplamente utilizada em metodologias ágeis como o XP (eXtreme Programming) e o Scrum; Prática para “Desenvolvimento de Software” que prega que todo e qualquer desenvolvimento deva ser precedido da criação de uma suíte de testes e que toda duplicação de código deve ser eliminada; Testes são desenvolvidos na mesma linguagem de programação do sistema em desenvolvimento; Testes quando criados podem até mesmo não compilar, neste momento eles não necessitam compilar; A única regra é que os testes sejam criados antes da implementação das funcionalidades do sistema;
  • 6.
    TDD - BenefíciosMelhora na interface das classes da aplicação; Maior desacoplamento das classes ; Aumento na Produtividade; Aumento da confiança pelo desenvolvedor em fazer alterações; Possibilidade de fazer Refactor (Refatoração de código); Cobertura de cenários da aplicação; Possibilidade de auto documentação a partir da Suite de Testes; Flexibilidade – maior facilidade em abraçar às mudanças nos requisitos; Deploy simplificado – g arantia de uma versão estável a todo o momento, pronta para subir para a homologação ou produção ;
  • 7.
    TDD – Oque não é? Ferramenta de teste; A Resolução de todos os problemas no desenvolvimento de sistemas; Padrões de Projeto (Design Patterns);
  • 8.
    Mitos sobre oTDD Test-Driven Development != QA Test-Driven Development != Testes Unitários Test-Driven Development != Design Patterns
  • 9.
    Prática do TDD0. Setup (somente executado uma vez no ciclo de vida do projeto); Red – Criar um teste que falhe (eliminar falsos positivos); Green – Criar a implementação mais simples possível que faça o teste passar; Refactor! – Refatorar a implementação, melhorando-a de modo incremental e controlada, fazendo com que o código comunique melhor o seu intento, eliminando duplicidades no código, fazendo as alterações arquiteturais (se estas se mostrarem necess á rias) e e xecutando os testes a cada alteração por menor que esta seja;
  • 10.
    E caso algumteste tenha falhado? Desfazer as últimas alterações, e refazer a funcionalidade de modo a não quebrar as funcionalidades construídas previamente; O ideal é que a aplicação não seja quebrada (testes em vermelho) por mais do que poucos minutos; Passos curtos, incrementais e controlados;
  • 11.
    Encontrei um bug,e agora? Criar um teste que exponha este cenário descoberto, realizar a prática do TDD ( Red – Green – Refactor! ), assim como qualquer outro teste previamente planejado; No longo prazo com a utilização desta prática teremos um produto cada vez melhor com uma quantidade maior de cenários cobertos, menos retrabalho e um número menor de bugs;
  • 12.
    Tudo Verde! Done!Após a criação da sua Suíte de testes e a implementação do código que faz com que todos estes testes passem (assim como os testes para resolução de bugs ), ou seja, tudo está verde, podemos concluir que o trabalho foi terminado para a iteração corrente;
  • 13.
    Dificuldades na implantaçãodo TDD na Empresa Investimento em treinamento; Grande barreiras culturais na empresa; Quebra de paradigma muito forte, pois põem em cheque conceitos maduros na área de Desenvolvimento de Software (considerados como verdades absolutas);
  • 14.
    O que istotem a ver com você? Mais e mais empresas estão abraçando a idéia de metodologias de desenvolvimento ágeis como XP e SCRUM; Demanda por profissionais que conheçam e já se utilizem destas metodologias e pr á ticas deve crescer ao longo dos próximos anos; Desenvolvedores e empresas que abraçarem a mudança e assumirem que os requisitos vão mudar e que a maneira como estamos gerenciando os nossos projetos não é produtiva, certamente se beneficiarão mais e estarão na ponta quando o mercado como um todo fizer a transição; Eu estarei lá. E você?
  • 15.
    Glossário TDD –Test-Driven Development – Desenvolvimento Dirigido a Testes. QA – Quality Assurance – Garantia da Qualidade. XP – eXtreme Programming – Programação Extrema é uma metodologia ágil de desenvolvimento de software ágil, criada por Kent Beck. SCRUM – é uma metodologia de gerenciamento de projetos, cujo nome vem de uma jogada do esporte rugby, em que os jogadores de cada time se encaixam formando uma espécie de muralha e onde outro jogador joga a bola no meio do túnel formado para que os dois grupos a disputem. Assim como em todas as metodologias ágeis o trabalho em equipe é fundamental.
  • 16.
    Solução – Criaçãode uma classe de Pilha – Stack Problema: Criar uma classe que represente uma pilha ilimitada em memória e que o acesso seja restrito ao último elemento inserido na pilha;
  • 17.
    Criar a TestList para a Pilha Analisando o requisito passado (Stack), foram observados os seguintes testes iniciais: Criar uma pilha e verificar que está vazia; Inserir um objeto na Pilha e verificar que não está vazia; Inserir um objeto na Pilha , retirar este, e verificar que está vazia; Inserir um objeto na Pilha (guardando o seu valor), retirar este da Pilha e verificar que estes são iguais; Inserir 3 objetos em sequência (guardando seus valores), e removê-los verificando se estes são removidos na ordem correta; Retirar um elemento de uma Pilha sem elementos; Inserir um objeto na Pilha, buscar quem é o topo da Pilha e verificar que a Pilha não está vazia; Inserir um objeto na Pilha (guardando o seu valor), buscar quem é o topo da Pilha e verificar que estes são iguais; Buscar o topo de uma Pilha vazia;
  • 18.
    Análise da TestListAnalisando a TestList vemos que existem 3 operações e uma propriedade que podem ser executadas na Pilha: Push(Object) (inserir um item na pilha); Pop() (remover um item da pilha, retonando-o); Top() (retornar o primeiro elemento sem retirá-lo da pilha); IsEmpty (retorna um booleano indicando se a pilha está vazia);
  • 19.
    Setup (executado somenteuma vez) Baixar do site http://www.nunit.org , o programa msi instalador; Executar o programa de instalação do Nunit; Criar um projeto de testes para a classe a ser criada Stack ; Referenciar a “dll” do framework de testes Nunit ( Nunit.Framework.dll ) no projeto de testes; Referenciar o projeto da classe a ser testada; Fazer deste projeto, o projeto inicial da Solução; Configurar a inicialização automática do programa Nunit.exe (interface visual dos testes unitários) na compilação do Projeto de Testes;
  • 20.
    Teste 1 Oprimeiro teste escolhido foi verificar se uma pilha nova está vazia: // StackTests .cs using System; using NUnit.Framework; [ TestFixture ] public class StackTests { [ Test ] public void Empty() { Stack stack = new Stack (); Assert .IsTrue(stack.IsEmpty); } } Executar a solução; Erros de compilação (Stack não existe);
  • 21.
    Teste 1 (Red ): Implementar o menor código que faça o teste compilar // Stack.cs using System; public class Stack { public Boolean IsEmpty { get { return false ; } } } Executar a solução; Solução compilada com sucesso; Teste Empty falha (Red);
  • 22.
    Teste 1 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; public class Stack { public Boolean IsEmpty { get { return true ; } } } Executar a solução; Solução compilada com sucesso; Teste Empty executado com sucesso;
  • 23.
    Teste 1 (Refactor! ): Melhorar a implementação // Stack.cs using System; public class Stack { public Stack() { this . _isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } } Executar a solução; Solução compilada com sucesso; Teste Empty continua executando com sucesso;
  • 24.
    Teste 2 Osegundo teste escolhido foi verificar se ao adicionar um elemento a pilha não está vazia: // StackTests .cs [ Test ] public void PushOne() { Stack stack = new Stack (); stack.Push( “primeiro elemento” ); Assert .IsFalse(stack.IsEmpty, “Após a inclusão, IsEmpty deve ser false.” ); } Executar a solução; Erros de compilação (Stack não possui o método Push(Object));
  • 25.
    Teste 2 (Red ): Implementar o menor código que faça o teste compilar // Stack.cs using System; public class Stack { public Stack() { this ._isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } public void Push( Object element_) { } } Executar a solução; Solução compilada com sucesso; Teste PushOne falha (Red);
  • 26.
    Teste 2 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; public class Stack { public Stack() { this ._isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } public void Push( Object element_) { this ._isEmpty = false; } } Executar a solução; Solução compilada com sucesso; Teste PushOne executado com sucesso;
  • 27.
    Teste 2 (Refactor! ): Melhorar a implementação // StackTests .cs using System; using NUnit.Framework; [ TestFixture ] public class StackTests { private Stack stack; [ SetUp ] public void Init() { stack = new Stack (); } [ Test ] public void Empty() { Assert .IsTrue(stack.IsEmpty); } [ Test ] public void PushOne() { stack.Push( “primeiro elemento” ); Assert .IsFalse(stack.IsEmpty, “Após um Push, IsEmpty deve ser false.” ); } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 28.
    Teste 3 Oterceiro teste escolhido foi verificar se ao adicionar um elemento e retirá-lo a pilha está vazia: // StackTests .cs [ Test ] public void PushAndPop() { stack.Push( “primeiro elemento” ); stack.Pop(); Assert .IsTrue(stack.IsEmpty, “Após uma operação Push - Pop, IsEmpty deve ser true.” ); } Executar a solução; Erros de compilação (Stack não possui o método Pop());
  • 29.
    Teste 3 (Red ): Implementar o menor código que faça o teste compilar // Stack.cs using System; public class Stack { public Stack() { this . _isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this . _isEmpty ; } } public void Push( Object element_) { this . _isEmpty = false ; } public void Pop() { } } Executar a solução; Solução compilada com sucesso; Teste PushAndPop falha (Red);
  • 30.
    Teste 3 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; public class Stack { public Stack() { this ._isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } public void Push( Object element_) { this ._isEmpty = false ; } public void Pop() { this ._isEmpty = true ; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 31.
    Teste 3 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 32.
    Teste 4 Oquarto teste escolhido foi adicionar um elemento (lembrando o seu valor), retirá-lo e verificar se o elemento é o mesmo: // StackTests .cs [ Test ] public void PushAndPopContentCheck() { Int32 expected = 1234; stack.Push(expected); Int32 actual = ( Int32 )stack.Pop(); Assert .AreEqual(expected , actual); } Executar a solução; Erros de compilação (o método Pop() retorna void);
  • 33.
    Teste 4 (Red ): Implementar o menor código que faça o teste compilar // Stack.cs using System; public class Stack { public Stack() { this ._isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } public void Push( Object element_) { this ._isEmpty = false ; } public Object Pop() { this . _isEmpty = true ; return null ; } } Executar a solução; Solução compilada com sucesso; Teste PushAndPopContentCheck falha (Red);
  • 34.
    Teste 4 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; public class Stack { private Object _element; public Stack() { this ._isEmpty = true ; } private Boolean _isEmpty; public Boolean IsEmpty { get { return this ._isEmpty; } } public void Push( Object element_) { this ._element = element_; this ._isEmpty = false ; } public Object Pop() { this ._isEmpty = true ; Object top = this ._element; this ._element = null ; return top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 35.
    Teste 4 (Refactor! ): Melhorar a implementação // Stack.cs using System; public class Stack { private Object _element; public Stack() { } public Boolean IsEmpty { get { return this ._element == null ; } } public void Push( Object element_) { this ._element = element_; } public Object Pop() { Object top = this ._element ; this ._element = null ; return top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 36.
    Teste 5 (Red ) O quinto teste escolhido foi adicionar 3 elementos (lembrando os seus valores), retirá-los e verificar se os elementos são os mesmos: // StackTests .cs [ Test ] public void PushAndPopMultipleContentCheck() { String pushed1 = “1” ; stack.Push(pushed1); String pushed2 = “2” ; stack.Push(pushed2); String pushed3 = “3” ; stack.Push(pushed3); String popped = stack.Pop() as String ; Assert .AreEqual(pushed3, popped); popped = stack.Pop() as String ; Assert .AreEqual(pushed2, popped); popped = stack.Pop() as String ; Assert .AreEqual(pushed1, popped); } Executar a solução; Solução compilada com sucesso; Teste PushAndPopMultipleContentCheck falha (Red);
  • 37.
    Teste 5 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; using System.Collections; public class Stack { private ArrayList _elements = new ArrayList (); public Boolean IsEmpty { get { return this ._elements.Count == 0; } } public void Push( Object element_) { this ._elements.Insert(0, element_); } public Object Pop() { Object top = this ._elements[0]; this ._elements.RemoveAt(0); return top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 38.
    Teste 5 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 39.
    Teste 6 (Red ) O sexto teste escolhido foi tentar retirar um elemento de uma pilha sem elementos: // StackTests .cs [ ExpectedException ( typeof ( InvalidOperationException ))] [ Test ] public void PopEmptyStack() { stack.Pop(); } Executar a solução; Solução compilada com sucesso; Teste PopEmptyStack falha (Red);
  • 40.
    Teste 6 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; using System.Collections; public class Stack { private ArrayList _elements = new ArrayList (); public Boolean IsEmpty { get { return this ._elements.Count == 0; } } public void Push( Object element_) { this ._elements.Insert(0, element_); } public Object Pop() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); Object top = this ._elements[0]; this ._elements.RemoveAt(0); return top; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 41.
    Teste 6 (Refactor! ): Melhorar a implementação Ao realizar este teste vieram à tona mais alguns testes que deveriam ser incluídos em nossa TestList: Inserir nulo em uma pilha e verificar que ela não está vazia. Inserir nulo em uma Pilha, retirá-lo e verificar que o valor retornado é nulo. Inserir nulo em uma Pilha, chamar Top e verificar que o valor retornado é nulo.
  • 42.
    Teste 7 Osétimo teste escolhido foi incluir um único objeto, chamar Top e verificar se a lista não está vazia: // StackTests .cs [ Test ] public void PushTop() { stack.Push(“42”); stack.Top(); Assert .IsFalse(stack.IsEmpty); } Executar a solução; Erros de compilação (o método Top() não existe);
  • 43.
    Teste 7 (Green ): Implementar o menor código que faça o teste compilar // Stack.cs using System; using System.Collections; public class Stack { private ArrayList _elements = new ArrayList (); public Boolean IsEmpty { get { return this ._elements.Count == 0; } } public void Push( Object element_) { this ._elements.Insert(0, element_); } public Object Pop() { if ( this . IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); Object top = this ._elements[0]; this ._elements.RemoveAt(0); return top; } public Object Top() { return null ; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 44.
    Teste 7 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 45.
    Teste 8 (Red ) O oitavo teste escolhido foi inserir um elemento na Pilha (guardando o seu valor), chamar Top e verificar que os elementos são iguais: // StackTests .cs [ Test ] public void PushTopContentCheckElement() { String pushed = “42” ; stack.Push(pushed); String topped = stack.Top() as String ; Assert .AreEqual(pushed, topped); } Executar a solução; Solução compilada com sucesso; Teste PushTopContentCheckElement falha (Red);
  • 46.
    Teste 8 (Green ): Fazer o teste passar com a mais simples implementação using System; using System.Collections; public class Stack { private ArrayList _elements = new ArrayList (); public Boolean IsEmpty { get { return this ._elements.Count == 0; } } public void Push( Object element_) { this ._elements.Insert(0, element_); } public Object Pop() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); Object top = this ._elements[0]; this ._elements.RemoveAt(0); return top; } public Object Top() { return this ._elements[0]; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 47.
    Teste 8 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 48.
    Teste 9 (Green ) O nono teste escolhido foi inserir múltiplos elementos na Pilha (guardando o valor do último inserido), chamar Top e verificar que os elementos são iguais: // StackTests .cs [ Test ] public void PushMultipleTopContentCheckElement() { String pushed1 = “1” ; stack.Push(pushed1); String pushed2 = “2” ; stack.Push(pushed2); String pushed3 = “3” ; stack.Push(pushed3); String topped = stack.Top() as String ; Assert .AreEqual(pushed3, topped); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 49.
    Teste 9 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 50.
    Teste 10 (Green ) O décimo teste chamar Top diversas vezes e verificar que os elementos são iguais: // StackTests .cs [ Test ] public void MultipleTopContentCheckElement() { String pushed = “1”; stack.Push(pushed); String topped1 = stack.Top() as String ; Assert .AreEqual(pushed , topped1); String topped2 = stack.Top() as String ; Assert .AreEqual(topped1, topped2); String topped3 = stack.Top() as String ; Assert .AreEqual(topped2, topped3); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 51.
    Teste 10 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 52.
    Teste 11 (Red ) O décimo primeiro teste é chamar Top em uma pilha vazia // StackTests .cs [ ExpectedException ( typeof ( InvalidOperationException ))] [ Test ] public void TopEmpty() { stack.Top(); } Executar a solução; Solução compilada com sucesso; Teste TopEmpty falha (Red);
  • 53.
    Teste 11 (Green ): Fazer o teste passar com a mais simples implementação // Stack.cs using System; using System.Collections; public class Stack { … public Object Pop() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); Object top = this ._elements[0]; this ._elements.RemoveAt(0); return top; } public Object Top() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); return this ._elements[0]; } } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 54.
    Teste 11 (Refactor! ): Melhorar a implementação … public Object Pop() { Object top = Top(); this ._elements.RemoveAt(0); return top; } public Object Top() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); return this ._elements[0]; } … Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 55.
    Teste 12 (Green ) O décimo segundo teste é inserir nulo e verificar que a pilha não está vazia. // StackTests .cs [ Test ] public void PushNull () { stack.Push( null ); Assert .IsFalse(stack.IsEmpty); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 56.
    Teste 12 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 57.
    Teste 13 (Green ) O décimo terceiro teste é inserir um objeto nulo chamar Pop e verificar que o valor retornado é nulo e que a pilha está vazia. // StackTests .cs [ Test ] public void PushNullCheckPop() { stack.Push( null ); Assert .IsNull(stack.Pop()); Assert .IsTrue(stack.IsEmpty); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 58.
    Teste 13 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 59.
    Teste 14 (Green ) O décimo quarto teste é inserir um objeto nulo chamar Top e verificar que o valor retornado é nulo. // StackTests .cs [ Test ] public void PushNullCheckTop() { stack.Push( null ); Assert .IsNull(stack.Top()); } Executar a solução; Solução compilada com sucesso; Após às alterações, todos os testes continuam passando com sucesso.
  • 60.
    Teste 14 (Refactor! ): Melhorar a implementação Neste ponto não há nenhuma replicação de código que possa ser retirada do código; Então seguimos em frente;
  • 61.
    Stack.cs using System; using System.Collections; public class Stack { private ArrayList _elements = new ArrayList (); public Boolean IsEmpty { get { return this ._elements.Count == 0; } } public void Push( Object element_) { this ._elements.Insert(0, element_); } public Object Pop() { Object top = Top(); this ._elements.RemoveAt(0); return top; } public Object Top() { if ( this .IsEmpty) throw new InvalidOperationException ( “A pilha está vazia.” ); return this ._elements[0]; } }