Post on 31-Jul-2015
Test-Driven Development
O que é? O que não é?
E o que isto tem a ver com você?
Carlos Eduardo MirandaArquiteto .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 – garantia 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 != QATest-Driven Development != Testes UnitáriosTest-Driven Development != Design Patterns
Prática do TDD0. Setup (somente executado uma vez no ciclo de vida do
projeto);1. Red – Criar um teste que falhe (eliminar falsos positivos);2. Green – Criar a implementação mais simples possível que
faça o teste passar;3. 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 executando 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:1. Criar uma pilha e verificar que está vazia;2. Inserir um objeto na Pilha e verificar que não está vazia;3. Inserir um objeto na Pilha , retirar este, e verificar que está vazia;4. Inserir um objeto na Pilha (guardando o seu valor), retirar este da Pilha e
verificar que estes são iguais;5. Inserir 3 objetos em sequência (guardando seus valores), e removê-los
verificando se estes são removidos na ordem correta;6. Retirar um elemento de uma Pilha sem elementos;7. Inserir um objeto na Pilha, buscar quem é o topo da Pilha e verificar que a
Pilha não está vazia;8. Inserir um objeto na Pilha (guardando o seu valor), buscar quem é o topo
da Pilha e verificar que estes são iguais;9. 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 1O 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.csusing 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.csusing 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.csusing 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 2O 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.csusing 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.csusing 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 3O 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.csusing 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.csusing 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 4O 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.csusing 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.csusing 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.csusing 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.csusing 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.csusing 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:10. Inserir nulo em uma pilha e verificar que ela não
está vazia.11. Inserir nulo em uma Pilha, retirá-lo e verificar
que o valor retornado é nulo.12. 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.csusing 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.csusing 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.csusing 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]; }
}