Test-Driven Development e
  sua Influência no Design
           Mauricio Aniche
      http://www.aniche.com.br
Mas o que é
TDD mesmo?
Red - Green - Refactor
É sobre testes?
NÃO!
É sobre design!
Mas que confusão!
Test-driven development (TDD) is the craft of producing
  automated tests for production code, and using that
process to drive design and programming. For every tiny
   bit of functionality in the production code, you first
develop a test that specifies and validates what the code
 will do.You then produce exactly as much code as will
enable that test to pass.Then you refactor (simplify and
  clarify) both the production code and the test code.




 www.agilealliance.org/programs/roadmaps/Roadmap/tdd/tdd_index.htm
mas todo mundo
já sabe que tdd é sobre
         design!
TDD mostra o
problema, mas não
 resolve pra você!
E como ele ajuda?
[TestFixture]
public class GeradorDeNotaFiscalTest
{
    [Test]
    public void DeveGerarUmaNotaFiscal {
         var gerador = new GeradorDeNotaFiscal();
         var nf = gerador.gera(fatura);
         Assert.AreEqual(fatura.Valor * 0.2, nf.ValorImposto);
    }
}




                                                  - hmm... ele depende de algo?
                                                    - deve receber uma fatura?
                                                - o nome do método está claro?
                                                    - o que ele deve retornar?
[TestFixture]
public class GeradorDeNotaFiscalTest
{
    [Test]
    public void DeveGerarUmaNotaFiscalEAgruparPorAliquota {
         var gerador = new GeradorDeNotaFiscal();
         var notas = gerador.gera(faturaComServicosComAliquotasDiferentes());
         Assert.AreEqual(1, notas.Count);
    }

    [Test]
    public void DeveGerarUmaNotaFiscalEAgruparPorSerie {
        var gerador = new GeradorDeNotaFiscal();
        var notas = gerador.gera(faturaComServicosComSeriesDiferentes());
        Assert.AreEqual(1, notas.Count);
    }
}                                                 hmm... esse agrupamento é complicado!
                                                   a implementação tá confusa por causa
                                                                     dele...
                                                             já sei! vou extrair!
[TestFixture]
public class GeradorDeNotaFiscalTest
{
    [Test]
    public void DeveGerarUmaNotaFiscal {
         var agrupador = new Mock<IAlgoritmoDeAgrupamento>();
         var gerador = new GeradorDeNotaFiscal(agrupador.Object);
         var notas = gerador.gera(fatura);
         Assert.AreEqual(1, notas.Count);
    }
}




                                                                    nice!
mas não é fácil!
na primeira vez...
public class Carro
{
   public Carro ()
   {
   }
}
public class Carro
{
   private Pneus pneuDianteiro;

    public Carro (Pneus pneuDianteiro)
    {
       this.pneuDianteiro = pneuDianteiro;
    }
}
public class Carro
{
   private Pneus pneuDianteiro;
   private Pneus pneuTraseiro;

    public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro)
    {
       this.pneuDianteiro = pneuDianteiro;
       this.pneuTraseiro = pneuTraseiro;
    }
}
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao)
       {
           this.pneuDianteiro = pneuDianteiro;
           this.pneuTraseiro = pneuTraseiro;
           this.suspensao = suspensao;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;
      private Freio freio;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor, Freio freio)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
          this.freio = freio;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;
      private Freio freio;
      private CaixaDeCambio caixaDeCambio;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
          this.freio = freio;
          this.caixaDeCambio = caixaDeCambio;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;
      private Freio freio;
      private CaixaDeCambio caixaDeCambio;
      private Combustivel combustivel;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio,
Combustivel combustivel)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
          this.freio = freio;
          this.caixaDeCambio = caixaDeCambio;
          this.combustivel = combustivel;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;
      private Freio freio;
      private CaixaDeCambio caixaDeCambio;
      private Combustivel combustivel;
      private Renavam renavam;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio,
Combustivel combustivel, Renavam renavam)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
          this.freio = freio;
          this.caixaDeCambio = caixaDeCambio;
          this.combustivel = combustivel;
          this.renavam = renavam;
       }
   }
public class Carro
   {
      private Pneus pneuDianteiro;
      private Pneus pneuTraseiro;
      private Suspensao suspensao;
      private Motor motor;
      private Freio freio;
      private CaixaDeCambio caixaDeCambio;
      private Combustivel combustivel;
      private Renavam renavam;

       public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao
suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio,
Combustivel combustivel, Renavam renavam, ...)
       {
          this.pneuDianteiro = pneuDianteiro;
          this.pneuTraseiro = pneuTraseiro;
          this.suspensao = suspensao;
          this.motor = motor;
          this.freio = freio;
          this.caixaDeCambio = caixaDeCambio;
          this.combustivel = combustivel;
          this.renavam = renavam;
          // ...
       }
   }
Mas quando você
aprende a usar...
Ouça seus testes!
Vou mostrar como
eu vejo o feedback
    dos testes.
Bloated constructor
Sintoma: A classe possui um alto acoplamento, e isso pode ser
visto através do número de parâmetros que a classe recebe no
construtor, por exemplo.
    [TestFixture]
    public class MessageProcessorTest
    {
         // atributos com as dependencias que serao mockadas

        [SetUp]
        public void SetUp() {
            // criando mocks
        }

         [Test]
         public void ShouldDoSomething()
         {
             var processor = new MessageProcessor(unpacker, auditer, locationFinder,
counterPartyFinder, domesticNotifier, importedNotifier);
             processor.OnMessage(BuildSomeSpecificRawMessage());
             // algumas assercoes aqui..
         }
    }
Bloated constructor
public class MessageProcessor
{

    public MessageProcessor(MessageUnpacker unpacker,
                     AuditTrail auditer,
                     CounterPartyFinder counterpartyFinder,
                     LocationFinder locationFinder,
                     DomesticNotifier domesticNotifier,
                     ImportedNotifier importedNotifier) {
        // seta os atributos aqui
    }

    public void OnMessage(Message rawMessage) {
        UnpackedMessage unpacked = unpacker.Unpack(rawMessage, counterpartyFinder);
        auditer.RecordReceiptOf(unpacked);
        // alguma outra atividade aqui
        if (locationFinder.IsDomestic(unpacked)) {
             domesticNotifier.Notify(unpacked.AsDomesticMessage());
        } else {
             importedNotifier.Notify(unpacked.AsImportedMessage());
        }
    }
}
Bloated constructor
public class MessageProcessor
{

    public MessageProcessor(MessageUnpacker unpacker,
                     AuditTrail auditer,
                     LocationFinder locationFinder,
                     DomesticNotifier domesticNotifier,
                     ImportedNotifier importedNotifier) {
        // seta os atributos aqui
    }

    public void OnMessage(Message rawMessage) {
        UnpackedMessage unpacked = unpacker.Unpack(rawMessage);
        auditer.RecordReceiptOf(unpacked);
        // some other activity here
        if (locationFinder.IsDomestic(unpacked)) {
             domesticNotifier.Notify(unpacked.AsDomesticMessage());
        } else {
             importedNotifier.Notify(unpacked.AsImportedMessage());
        }
    }
}
Bloated constructor
public class MessageProcessor
{

    public MessageProcessor(MessageUnpacker unpacker,
                     AuditTrail auditer,
                     MessageDispatcher dispatcher) {
        // seta os atributos aqui
    }

    public void OnMessage(Message rawMessage) {
        UnpackedMessage unpacked = unpacker.Unpack(rawMessage);
        auditer.RecordReceiptOf(unpacked);
        // alguma outra atividade aqui
        dispatcher.Dispatch(unpacked);
    }
}
mas e o exemplo
   do carro?
public class Carro
    {
        private Pneus pneuDianteiro;
        private Pneus pneuTraseiro;
        private Suspensao suspensao;
        private Motor motor;
        private Freio freio;
        private CaixaDeCambio caixaDeCambio;
        private Combustivel combustivel;

         public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao,
Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, ...)
         {
             this.pneuDianteiro = pneuDianteiro;
             this.pneuTraseiro = pneuTraseiro;
             this.suspensao = suspensao;
             this.motor = motor;
             this.freio = freio;
             this.caixaDeCambio = caixaDeCambio;
             this.combustivel = combustivel;
             ...
         }
    }
dependencies,
 notifications,
 adjustments
public class Carro
    {
        private Pneus pneuDianteiro;
        private Pneus pneuTraseiro;
        private Suspensao suspensao;
        private Motor motor;
        private Freio freio;
        private CaixaDeCambio caixaDeCambio;
        private Combustivel combustivel;
        private Renavam renavam;

        public Carro (Renavam renavam)
        {
            this.renavam = renavam;
            this.pneuDianteiro = Pneus.Firestone();
            this.pneuTraseiro = Pneus.Michellin();
            this.suspensao = Suspensao.AMelhor();
            this.motor = Motor.DezesseisValvulas();
            this.freio = Freio.ABS();
            this.caixaDeCambio = CaixaDeCambio.Automatica();
            this.combustivel = Combustivel.Flex();
        }

         public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao,
Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, Renavam
renavam)
         {
            // seta os atributos
         }

    }
use um builder!
new CarroBuilder().ComPneu(Pneus.Firestone).ComMotor(Motor.Zetec).[...].Cria();




                                                                       by @cv
Mocks de mocks de mocks...
Sintoma: Você precisa fazer mocks de mocks (de mocks ...)
para conseguir testar o que precisa.
    [TestFixture]
    public class AcceptPostPutAndPatchVerbsTests
    {
        // atributos

        [SetUp]
        public void SetupAContext()
        {
            httpContext = new Mock<HttpContextBase>();
            httpRequest = new Mock<HttpRequestBase>();
            requestContext = new RequestContext(httpContext.Object, new RouteData());
            controllerContext = new ControllerContext(httpContext.Object, new RouteData(), new
SomeController());
            httpContext.Setup(h => h.Request).Returns(httpRequest.Object);
            context = new ActionExecutingContext(controllerContext, new
Mock<ActionDescriptor>().Object, new RouteValueDictionary()) { RequestContext = requestContext };
        }

        [Test]
        public void ShouldAcceptPost()
        {
            httpRequest.Setup(h => h.HttpMethod).Returns("POST");

            Assert.IsTrue(new AcceptPostPutAndPatchVerbs().IsValid(context));
        }
    }
Lei de Demeter
  [nada de A.B.metodoC() ...]
Cuidado com herança
 Lembre-se do Princípio de Substituição de Liskov (LSP)
Mocks que não são usados
           diretamente pelo teste
Sintoma: Você passa um mock, seta uma expectativa para ele,
mas acaba não os usando nas asserções do teste.

   [TestFixture]
                                                      
   public class GeradorDeNotaFiscal

   public class GeradorDeNotasFiscaisTest
                                                      
   {

   {

   
   [SetUp]
                                                      
   
   public GeradorDeNotaFiscal

   
   public void SetUp() {
                                                      (InformacoesFiscais info, Sap sap,

   
   
   sap = new Mock<SAP>();
                                                      EnviadorDeEmail emails)

   
   
   emails = new Mock<EnviadorDeEmail>();
                                                      
   
   {

   
   
   info = new Mock<InformacoesFiscais>();
                                                      
   
   
   // seta atributos

   
   
   gerador = new GeradorDeNotaFiscal(info,
                                                      
   
   }
sap.Object, emails.Object);
                                                      
   

   
   }
                                                      
   
   public NotaFiscal Gera(Fatura fatura) {

   
                                                      
   
   
   var nota = geraNotaUsandoInfo();

   
   [Test] public void DeveEnviarAoSap () {
                                                      
   
   

   
   
   var nf = gerador.gera(fatura);
                                                      
   
   
   sap.EnviaNota(nota);

   
   
   sap.Verify(s => s.EnviaNota(nf),
                                                      
   
   
   emails.EnviaNota(nota);
Times.Once());
                                                      
   
   

   
   }
                                                      
   
   }
                                                      
   }

   
   [Test] public void DeveEnviarOEmail () {

   
   
   var nf = gerador.gera(fatura);

   
   
   emails.Verify(s => s.EnviaNota(nf),
Times.Once());

   
   }}
Single Responsibility
   Principle (SRP)
  (A classe deve ter apenas uma responsabilidade)
public class GeradorDeNotaFiscal

   {

   
 private IList<IObservadorDeNotaGerada> observadores;


   
   public GeradorDeNotaFiscal (InformacoesFiscais info)

   
   {

   
   
 // seta atributos

   
   }


   
   public void AdicionaObservador(IObservadorDeNotaGerada observador) { ... }

   

   
   private void Notifica(NotaFiscal nota) {

   
   
 foreach(var observador in observadores) {
              observador.notifica(nota);
           }

   
   }


   
   public NotaFiscal Gera(Fatura fatura) {

   
   
 var nota = geraNotaUsandoInformacoesFiscais();

   
   

   
   
 Notifica(nota);

   
   }

   }
O teste mostra, mas quem
refatora (para padrões) é você!
Intimidade Inapropriada
Sintoma: Você testa classes (geralmente com o péssimo sufixo
“Service”) que praticamente só lidam com uma classe específica.

   [TestFixture]

   public class EmiteBoletoServiceTest

   {


   
   [Test]

   
   public void DeveMarcarOBoletoComoEmitido ()

   
   {

   
   
 var service = new EmiteBoletoService();

   
   
 var boleto = new Boleto { Pago = true };

   
   
 service.Emite(boleto);

   
   

   
   
 Assert.AreEqual(true, boleto.Emitido);

   
   }

   }
                                                  
   public class EmiteBoletoService
                                                  
   {
                                                  
   
 public Emite(Boleto boleto) {
                                                  
   
 
 if(boleto.Pago) {
                                                  
   
 
 
 boleto.Emitido = true;
                                                  
   
 
 }
                                                  
   
 }
                                                  
   }
Tell, Don’t Ask!
(Você deve dizer ao objeto o que quer que ele faça!)
Cuidado com interfaces que
 interagem somente com o
       mesmo objeto!
    
   public interface BoletoService
    
   {

    
   
   void Emite(Boleto boleto);
    
   
   void Paga(Boleto boleto);
    
   
   void Cancela(Boleto boleto);
            ...
    
   }
Testando combinações
Sintoma: Os testes passam a ficar complicados e você passa a
testar diferentes combinações de entradas.

       public IList<Fatura> PegaFaturas() {

       
 var lista = repositorio.PegaTodasFaturas();

       
 lista = RemoveAntigas(lista);

       
 lista = RemovePagas(lista);

       
 lista = RemoveQuemTemContatoComercial(lista);

       
 return lista;
        }


       [Test]

       public void DeveRemoverAntigas() {

       
 MockaRepositorioParaDevolvarListaComFaturasAntigas();

       
 var lista = PegaFaturas();

       
 Assert.VerificaQueSóTemFaturasAntigas();
    }


       [Test]

       public void DeveRemoverPagas() {

       
 MockaRepositorioParaDevolvarListaComFaturasPagas();

       
 var lista = PegaFaturas();

       
 Assert.VerificaQueSóTemFaturasPagas();
    }

    ...
Crie classes especializadas!
public IList<Fatura> PegaFaturas() {

    
 var lista = repositorio.PegaTodasFaturas();

    
 return new RemoveAntigas(
                new RemovePagas(
                new RemoveQuemTemContatoComercial())).Filtra(lista);
     }



    public class RemoveAntigas : FiltroDeFatura {

    
 public RemoveAntigas(FiltroDeFatura) { ... }

    
 public RemoveAntigas() { ... }


    
    public Filtra(IList<Fatura> faturas) {
            // filtra e passa para o proximo!
          }
     }

    ...
Objetos insubstituíveis
Sintoma: Você precisa mockar um objeto que não é
substituível sem mágica!


   [Test]

   public void DeveEmitirAPassagemComADataAtual() {

   
 var passagem = new GeradorDePassagem().Emite();

   
 Assert.AreEqual(DateTime.Now, passagem.DataEmissao);
    }
Abstração é o segredo!
public class GeradorDePassagem {

   
 public Passagem(IRelogio relogio) { ... }


   
   public Passagem Emite(Passageiro passageiro) {
          var data = relogio.DataAtual;
          // faz alguma coisa com a data...
        }
    }




   [Test]

   public void DeveEmitirAPassagemComADataAtual() {

   
 var agora = DateTime.Now;

   
 relogio.SetupGet(r => r.DataAtual).Returns(agora);


   
   var passagem = new GeradorDePassagem(relogio).Emite();


   
   Assert.AreEqual(agora, passagem.DataEmissao);
    }
Evite singletons!
Nomenclatura ajuda!
Sintoma: O nome da classe e/ou seus métodos não fazem
sentido para o domínio!


   [Test]

   public void DeveFazerAlgumaCoisa() {

   
 var entidade = new GerenciadorServiceImpl(new PassagemDAO()).FazTarefa();
    }
Impl classes are meaningless!
Métodos FazXEY()
If’s e switches
Sintoma: Você tem testes muito parecidos que geram
resultados diferentes, e quando olha a implementação, ela tem
um if ou switch.

   [Test]

   public void CalculaISS() {

   
 var valor = new CalculaImposto().ParaValor(1500);

   
 Assert.AreEqual(1500*1.2, valor);
    }


   [Test]

   public void CalculaICMS() {

   
 var valor = new CalculaImposto().ParaValor(6000);

   
 Assert.AreEqual(6000*1.3, valor);
    }
public class CalculaImposto {

   
 private TipoImposto tipo;


   
   public CalculaImposto(TipoImposto tipo) {
           this.tipo = tipo;
         }


   
   public double ParaValor(double valor) {
           if(valor < 1000) {
             return valor * 1.1;
           }
           else if(valor > 1000 && valor < 5000) {
              return valor * 1.2;
           }
           else if (valor >= 5000 && valor < 8000) {
              return valor * 1.3;
           }
           else { ... }
        }
    }
Elimine if’s e switches na
   medida do possível!
Padrões de projeto podem
    ajudar (de novo)!
Brain classes
Sintoma: Você tem uma classe de teste muito grande.

   [Test]
public   void   DeveFazerX1() { ... }

   [Test]
public   void   DeveFazerX2() { ... }

   [Test]
public   void   DeveFazerX3() { ... }

   [Test]
public   void   DeveFazerX4() { ... }

   [Test]
public   void   DeveFazerX5() { ... }

   [Test]
public   void   DeveFazerX6() { ... }

   [Test]
public   void   DeveFazerX7() { ... }

   [Test]
public   void   DeveFazerX8() { ... }

   [Test]
public   void   DeveFazerX9() { ... }

   [Test]
public   void   DeveFazerX10() { ... }

   [Test]
public   void   DeveFazerX11() { ... }

   [Test]
public   void   DeveFazerX12() { ... }

   [Test]
public   void   DeveFazerX13() { ... }

   [Test]
public   void   DeveFazerX14() { ... }

   [Test]
public   void   DeveFazerX15() { ... }

   [Test]
public   void   DeveFazerX16() { ... }

   [Test]
public   void   DeveFazerX17() { ... }

   [Test]
public   void   DeveFazerX18() { ... }

   [Test]
public   void   DeveFazerX19() { ... }

   [Test]
public   void   DeveFazerX20() { ... }

   [Test]
public   void   DeveFazerXn() { ... }
A mesma coisa se você tem um
cenário muito grande para um
        simples teste!
A mesma coisa se você achar
que precisa testar um método
           privado!
Quebre essa classe em
pequenas outras classes!
       (lembre do SRP novamente!)
Mas é assim que
 eu interpreto!
Você pode achar outras
maneiras de interpretar!
        e se achar, me conta! ;)
"Every pattern describes a problem which occurs over
and over again in our environment, and then describes
the core of the solution to that problem, in such a way
that you can use this solution a million times over,
without ever doing it the same way twice."

Christopher Alexander, "The Pattern Language"
Ouça seus testes!
  deu pra ver o quanto eles têm pra contar?
Referências
- Freeman, S.; Pryce, N. “Growing Object Oriented Software, Guided By Tests.”
- Beck, K. “Test-Driven Development by Example”
- Gamma, E.; Helm, R.; Johnson, R.; Vlissides, J. “Design Patterns: Elements of
Reusable Object-Oriented Software”
- Feathers, M. “Working Effectively with Legacy Code”
- Martin, R. “Agile Principles, Patterns, and Practices in C#”
- Martin, R. “Clean Code”
Obrigado!
@mauricioaniche

Test-Driven Development e sua influência no design

  • 1.
    Test-Driven Development e sua Influência no Design Mauricio Aniche http://www.aniche.com.br
  • 2.
    Mas o queé TDD mesmo?
  • 3.
    Red - Green- Refactor
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
    Test-driven development (TDD)is the craft of producing automated tests for production code, and using that process to drive design and programming. For every tiny bit of functionality in the production code, you first develop a test that specifies and validates what the code will do.You then produce exactly as much code as will enable that test to pass.Then you refactor (simplify and clarify) both the production code and the test code. www.agilealliance.org/programs/roadmaps/Roadmap/tdd/tdd_index.htm
  • 9.
    mas todo mundo jásabe que tdd é sobre design!
  • 10.
    TDD mostra o problema,mas não resolve pra você!
  • 11.
    E como eleajuda?
  • 12.
    [TestFixture] public class GeradorDeNotaFiscalTest { [Test] public void DeveGerarUmaNotaFiscal { var gerador = new GeradorDeNotaFiscal(); var nf = gerador.gera(fatura); Assert.AreEqual(fatura.Valor * 0.2, nf.ValorImposto); } } - hmm... ele depende de algo? - deve receber uma fatura? - o nome do método está claro? - o que ele deve retornar?
  • 13.
    [TestFixture] public class GeradorDeNotaFiscalTest { [Test] public void DeveGerarUmaNotaFiscalEAgruparPorAliquota { var gerador = new GeradorDeNotaFiscal(); var notas = gerador.gera(faturaComServicosComAliquotasDiferentes()); Assert.AreEqual(1, notas.Count); } [Test] public void DeveGerarUmaNotaFiscalEAgruparPorSerie { var gerador = new GeradorDeNotaFiscal(); var notas = gerador.gera(faturaComServicosComSeriesDiferentes()); Assert.AreEqual(1, notas.Count); } } hmm... esse agrupamento é complicado! a implementação tá confusa por causa dele... já sei! vou extrair!
  • 14.
    [TestFixture] public class GeradorDeNotaFiscalTest { [Test] public void DeveGerarUmaNotaFiscal { var agrupador = new Mock<IAlgoritmoDeAgrupamento>(); var gerador = new GeradorDeNotaFiscal(agrupador.Object); var notas = gerador.gera(fatura); Assert.AreEqual(1, notas.Count); } } nice!
  • 15.
    mas não éfácil!
  • 16.
  • 17.
    public class Carro { public Carro () { } }
  • 18.
    public class Carro { private Pneus pneuDianteiro; public Carro (Pneus pneuDianteiro) { this.pneuDianteiro = pneuDianteiro; } }
  • 19.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; } }
  • 20.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; } }
  • 21.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; } }
  • 22.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; } }
  • 23.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; this.caixaDeCambio = caixaDeCambio; } }
  • 24.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; private Combustivel combustivel; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; this.caixaDeCambio = caixaDeCambio; this.combustivel = combustivel; } }
  • 25.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; private Combustivel combustivel; private Renavam renavam; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, Renavam renavam) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; this.caixaDeCambio = caixaDeCambio; this.combustivel = combustivel; this.renavam = renavam; } }
  • 26.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; private Combustivel combustivel; private Renavam renavam; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, Renavam renavam, ...) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; this.caixaDeCambio = caixaDeCambio; this.combustivel = combustivel; this.renavam = renavam; // ... } }
  • 27.
  • 28.
  • 29.
    Vou mostrar como euvejo o feedback dos testes.
  • 30.
    Bloated constructor Sintoma: Aclasse possui um alto acoplamento, e isso pode ser visto através do número de parâmetros que a classe recebe no construtor, por exemplo. [TestFixture] public class MessageProcessorTest { // atributos com as dependencias que serao mockadas [SetUp] public void SetUp() { // criando mocks } [Test] public void ShouldDoSomething() { var processor = new MessageProcessor(unpacker, auditer, locationFinder, counterPartyFinder, domesticNotifier, importedNotifier); processor.OnMessage(BuildSomeSpecificRawMessage()); // algumas assercoes aqui.. } }
  • 31.
    Bloated constructor public classMessageProcessor { public MessageProcessor(MessageUnpacker unpacker, AuditTrail auditer, CounterPartyFinder counterpartyFinder, LocationFinder locationFinder, DomesticNotifier domesticNotifier, ImportedNotifier importedNotifier) { // seta os atributos aqui } public void OnMessage(Message rawMessage) { UnpackedMessage unpacked = unpacker.Unpack(rawMessage, counterpartyFinder); auditer.RecordReceiptOf(unpacked); // alguma outra atividade aqui if (locationFinder.IsDomestic(unpacked)) { domesticNotifier.Notify(unpacked.AsDomesticMessage()); } else { importedNotifier.Notify(unpacked.AsImportedMessage()); } } }
  • 32.
    Bloated constructor public classMessageProcessor { public MessageProcessor(MessageUnpacker unpacker, AuditTrail auditer, LocationFinder locationFinder, DomesticNotifier domesticNotifier, ImportedNotifier importedNotifier) { // seta os atributos aqui } public void OnMessage(Message rawMessage) { UnpackedMessage unpacked = unpacker.Unpack(rawMessage); auditer.RecordReceiptOf(unpacked); // some other activity here if (locationFinder.IsDomestic(unpacked)) { domesticNotifier.Notify(unpacked.AsDomesticMessage()); } else { importedNotifier.Notify(unpacked.AsImportedMessage()); } } }
  • 33.
    Bloated constructor public classMessageProcessor { public MessageProcessor(MessageUnpacker unpacker, AuditTrail auditer, MessageDispatcher dispatcher) { // seta os atributos aqui } public void OnMessage(Message rawMessage) { UnpackedMessage unpacked = unpacker.Unpack(rawMessage); auditer.RecordReceiptOf(unpacked); // alguma outra atividade aqui dispatcher.Dispatch(unpacked); } }
  • 34.
    mas e oexemplo do carro?
  • 35.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; private Combustivel combustivel; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, ...) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; this.suspensao = suspensao; this.motor = motor; this.freio = freio; this.caixaDeCambio = caixaDeCambio; this.combustivel = combustivel; ... } }
  • 36.
  • 37.
    public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; private Suspensao suspensao; private Motor motor; private Freio freio; private CaixaDeCambio caixaDeCambio; private Combustivel combustivel; private Renavam renavam; public Carro (Renavam renavam) { this.renavam = renavam; this.pneuDianteiro = Pneus.Firestone(); this.pneuTraseiro = Pneus.Michellin(); this.suspensao = Suspensao.AMelhor(); this.motor = Motor.DezesseisValvulas(); this.freio = Freio.ABS(); this.caixaDeCambio = CaixaDeCambio.Automatica(); this.combustivel = Combustivel.Flex(); } public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro, Suspensao suspensao, Motor motor, Freio freio, CaixaDeCambio caixaDeCambio, Combustivel combustivel, Renavam renavam) { // seta os atributos } }
  • 38.
    use um builder! newCarroBuilder().ComPneu(Pneus.Firestone).ComMotor(Motor.Zetec).[...].Cria(); by @cv
  • 39.
    Mocks de mocksde mocks... Sintoma: Você precisa fazer mocks de mocks (de mocks ...) para conseguir testar o que precisa. [TestFixture]     public class AcceptPostPutAndPatchVerbsTests     {         // atributos [SetUp]         public void SetupAContext()         {             httpContext = new Mock<HttpContextBase>();             httpRequest = new Mock<HttpRequestBase>();             requestContext = new RequestContext(httpContext.Object, new RouteData());             controllerContext = new ControllerContext(httpContext.Object, new RouteData(), new SomeController());             httpContext.Setup(h => h.Request).Returns(httpRequest.Object);             context = new ActionExecutingContext(controllerContext, new Mock<ActionDescriptor>().Object, new RouteValueDictionary()) { RequestContext = requestContext };         } [Test]         public void ShouldAcceptPost()         {             httpRequest.Setup(h => h.HttpMethod).Returns("POST");             Assert.IsTrue(new AcceptPostPutAndPatchVerbs().IsValid(context));         }     }
  • 40.
    Lei de Demeter [nada de A.B.metodoC() ...]
  • 41.
    Cuidado com herança Lembre-se do Princípio de Substituição de Liskov (LSP)
  • 42.
    Mocks que nãosão usados diretamente pelo teste Sintoma: Você passa um mock, seta uma expectativa para ele, mas acaba não os usando nas asserções do teste. [TestFixture] public class GeradorDeNotaFiscal public class GeradorDeNotasFiscaisTest { { [SetUp] public GeradorDeNotaFiscal public void SetUp() { (InformacoesFiscais info, Sap sap, sap = new Mock<SAP>(); EnviadorDeEmail emails) emails = new Mock<EnviadorDeEmail>(); { info = new Mock<InformacoesFiscais>(); // seta atributos gerador = new GeradorDeNotaFiscal(info, } sap.Object, emails.Object); } public NotaFiscal Gera(Fatura fatura) { var nota = geraNotaUsandoInfo(); [Test] public void DeveEnviarAoSap () { var nf = gerador.gera(fatura); sap.EnviaNota(nota); sap.Verify(s => s.EnviaNota(nf), emails.EnviaNota(nota); Times.Once()); } } } [Test] public void DeveEnviarOEmail () { var nf = gerador.gera(fatura); emails.Verify(s => s.EnviaNota(nf), Times.Once()); }}
  • 43.
    Single Responsibility Principle (SRP) (A classe deve ter apenas uma responsabilidade)
  • 44.
    public class GeradorDeNotaFiscal { private IList<IObservadorDeNotaGerada> observadores; public GeradorDeNotaFiscal (InformacoesFiscais info) { // seta atributos } public void AdicionaObservador(IObservadorDeNotaGerada observador) { ... } private void Notifica(NotaFiscal nota) { foreach(var observador in observadores) { observador.notifica(nota); } } public NotaFiscal Gera(Fatura fatura) { var nota = geraNotaUsandoInformacoesFiscais(); Notifica(nota); } }
  • 45.
    O teste mostra,mas quem refatora (para padrões) é você!
  • 46.
    Intimidade Inapropriada Sintoma: Vocêtesta classes (geralmente com o péssimo sufixo “Service”) que praticamente só lidam com uma classe específica. [TestFixture] public class EmiteBoletoServiceTest { [Test] public void DeveMarcarOBoletoComoEmitido () { var service = new EmiteBoletoService(); var boleto = new Boleto { Pago = true }; service.Emite(boleto); Assert.AreEqual(true, boleto.Emitido); } } public class EmiteBoletoService { public Emite(Boleto boleto) { if(boleto.Pago) { boleto.Emitido = true; } } }
  • 47.
    Tell, Don’t Ask! (Vocêdeve dizer ao objeto o que quer que ele faça!)
  • 48.
    Cuidado com interfacesque interagem somente com o mesmo objeto! public interface BoletoService { void Emite(Boleto boleto); void Paga(Boleto boleto); void Cancela(Boleto boleto); ... }
  • 49.
    Testando combinações Sintoma: Ostestes passam a ficar complicados e você passa a testar diferentes combinações de entradas. public IList<Fatura> PegaFaturas() { var lista = repositorio.PegaTodasFaturas(); lista = RemoveAntigas(lista); lista = RemovePagas(lista); lista = RemoveQuemTemContatoComercial(lista); return lista; } [Test] public void DeveRemoverAntigas() { MockaRepositorioParaDevolvarListaComFaturasAntigas(); var lista = PegaFaturas(); Assert.VerificaQueSóTemFaturasAntigas(); } [Test] public void DeveRemoverPagas() { MockaRepositorioParaDevolvarListaComFaturasPagas(); var lista = PegaFaturas(); Assert.VerificaQueSóTemFaturasPagas(); } ...
  • 50.
  • 51.
    public IList<Fatura> PegaFaturas(){ var lista = repositorio.PegaTodasFaturas(); return new RemoveAntigas( new RemovePagas( new RemoveQuemTemContatoComercial())).Filtra(lista); } public class RemoveAntigas : FiltroDeFatura { public RemoveAntigas(FiltroDeFatura) { ... } public RemoveAntigas() { ... } public Filtra(IList<Fatura> faturas) { // filtra e passa para o proximo! } } ...
  • 52.
    Objetos insubstituíveis Sintoma: Vocêprecisa mockar um objeto que não é substituível sem mágica! [Test] public void DeveEmitirAPassagemComADataAtual() { var passagem = new GeradorDePassagem().Emite(); Assert.AreEqual(DateTime.Now, passagem.DataEmissao); }
  • 53.
  • 54.
    public class GeradorDePassagem{ public Passagem(IRelogio relogio) { ... } public Passagem Emite(Passageiro passageiro) { var data = relogio.DataAtual; // faz alguma coisa com a data... } } [Test] public void DeveEmitirAPassagemComADataAtual() { var agora = DateTime.Now; relogio.SetupGet(r => r.DataAtual).Returns(agora); var passagem = new GeradorDePassagem(relogio).Emite(); Assert.AreEqual(agora, passagem.DataEmissao); }
  • 55.
  • 56.
    Nomenclatura ajuda! Sintoma: Onome da classe e/ou seus métodos não fazem sentido para o domínio! [Test] public void DeveFazerAlgumaCoisa() { var entidade = new GerenciadorServiceImpl(new PassagemDAO()).FazTarefa(); }
  • 57.
    Impl classes aremeaningless!
  • 58.
  • 59.
    If’s e switches Sintoma:Você tem testes muito parecidos que geram resultados diferentes, e quando olha a implementação, ela tem um if ou switch. [Test] public void CalculaISS() { var valor = new CalculaImposto().ParaValor(1500); Assert.AreEqual(1500*1.2, valor); } [Test] public void CalculaICMS() { var valor = new CalculaImposto().ParaValor(6000); Assert.AreEqual(6000*1.3, valor); }
  • 60.
    public class CalculaImposto{ private TipoImposto tipo; public CalculaImposto(TipoImposto tipo) { this.tipo = tipo; } public double ParaValor(double valor) { if(valor < 1000) { return valor * 1.1; } else if(valor > 1000 && valor < 5000) { return valor * 1.2; } else if (valor >= 5000 && valor < 8000) { return valor * 1.3; } else { ... } } }
  • 61.
    Elimine if’s eswitches na medida do possível!
  • 62.
    Padrões de projetopodem ajudar (de novo)!
  • 63.
    Brain classes Sintoma: Vocêtem uma classe de teste muito grande. [Test] public void DeveFazerX1() { ... } [Test] public void DeveFazerX2() { ... } [Test] public void DeveFazerX3() { ... } [Test] public void DeveFazerX4() { ... } [Test] public void DeveFazerX5() { ... } [Test] public void DeveFazerX6() { ... } [Test] public void DeveFazerX7() { ... } [Test] public void DeveFazerX8() { ... } [Test] public void DeveFazerX9() { ... } [Test] public void DeveFazerX10() { ... } [Test] public void DeveFazerX11() { ... } [Test] public void DeveFazerX12() { ... } [Test] public void DeveFazerX13() { ... } [Test] public void DeveFazerX14() { ... } [Test] public void DeveFazerX15() { ... } [Test] public void DeveFazerX16() { ... } [Test] public void DeveFazerX17() { ... } [Test] public void DeveFazerX18() { ... } [Test] public void DeveFazerX19() { ... } [Test] public void DeveFazerX20() { ... } [Test] public void DeveFazerXn() { ... }
  • 64.
    A mesma coisase você tem um cenário muito grande para um simples teste!
  • 65.
    A mesma coisase você achar que precisa testar um método privado!
  • 66.
    Quebre essa classeem pequenas outras classes! (lembre do SRP novamente!)
  • 67.
    Mas é assimque eu interpreto!
  • 68.
    Você pode acharoutras maneiras de interpretar! e se achar, me conta! ;)
  • 69.
    "Every pattern describesa problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice." Christopher Alexander, "The Pattern Language"
  • 70.
    Ouça seus testes! deu pra ver o quanto eles têm pra contar?
  • 71.
    Referências - Freeman, S.;Pryce, N. “Growing Object Oriented Software, Guided By Tests.” - Beck, K. “Test-Driven Development by Example” - Gamma, E.; Helm, R.; Johnson, R.; Vlissides, J. “Design Patterns: Elements of Reusable Object-Oriented Software” - Feathers, M. “Working Effectively with Legacy Code” - Martin, R. “Agile Principles, Patterns, and Practices in C#” - Martin, R. “Clean Code”
  • 72.

Notas do Editor

  • #2 meu nome eh .. aluno de mestrado, onde pesquiso sobre tdd trablho na locaweb
  • #4 - Famoso ciclo - Adicione um teste; Rode todos os testes e veja o novo teste falhar; Fa&amp;#xE7;a uma pequena altera&amp;#xE7;&amp;#xE3;o; Rode todos os testes e veja todos passarem; Refatore para remover duplica&amp;#xE7;&amp;#xE3;o.
  • #5 tem teste no nome...
  • #7 - Transforma o teste em uma atividade de design, onde os programadores os utilizam para esclarecer as expectativas sobre o que um peda&amp;#xE7;o de c&amp;#xF3;digo deve fazer; mais do que isso, faz vc pensar tamb&amp;#xE9;m no design, e poder mudar de id&amp;#xE9;ia sobre ele (feedback); - Ajuda a resolver um dos maiores problemas do desenvolvimento de software, que &amp;#xE9; o gerenciamento de depend&amp;#xEA;ncias
  • #8 ter &amp;#x201C;test&amp;#x201D; no nome confunde! muitas defini&amp;#xE7;&amp;#xF5;es que s&amp;#xF3; levam em conta o fato de vc escrever um teste antes (igual a do JUnit in action)
  • #10 agora todo mundo j&amp;#xE1; sabe... em todo lugar, voc&amp;#xEA; encontra pessoas falando que TDD n&amp;#xE3;o &amp;#xE9; sobre testes e sim sobre design!
  • #11 - outra coisa que est&amp;#xE1; claro eh que ninguem mais acha q tdd eh a solucao - TDD mostra o problema atraves do feedback dos testes, e a experiencia do programador faz a diferenca para resolver
  • #12 - sinergia grande entre c&amp;#xF3;digo f&amp;#xE1;cil de testar e bom design - como vimos no exemplo acima, TDD faz com que as depend&amp;#xEA;ncias fiquem naturalmente expl&amp;#xED;citas - al&amp;#xE9;m disso, faz com que os comportamentos sejam invocados de forma mais conveniente.
  • #13 seus testes provem feedback para vc... vc pensa nas dependencias que essa classe tem, na interface que ela prover&amp;#xE1; para seus clientes e etc...
  • #14 quando os testes passam a ficar complicados, sempre h&amp;#xE1; uma refatora&amp;#xE7;&amp;#xE3;o para fazer!
  • #15 novas classes emergem a partir do feedback dos testes! a composicao eh mais simples que a soma das partes
  • #16 pensar em um design um pouco mais avancado nao eh facil.. como falei, vai da experiencia do programador...
  • #17 n&amp;#xE3;o &amp;#xE9; t&amp;#xE3;o f&amp;#xE1;cil assim usar o feedback dos testes para guiar o desenvolvimento...
  • #18 a primeira vez que vc faz TDD, vc &amp;#xE9; meio que obrigado a deixar as dependencias expl&amp;#xED;citas.. e &amp;#xE9; isso que vc faz...
  • #27 e quando voc&amp;#xEA; v&amp;#xEA; , voc&amp;#xEA; tem uma classe com MUITAS depend&amp;#xEA;ncias, o que faz com que a dificulta a manuten&amp;#xE7;&amp;#xE3;o da mesma.
  • #29 os testes prov&amp;#xEA;m um feedback imenso sobre o seu design!
  • #30 n&amp;#xE3;o h&amp;#xE1; muito assunto sobre isso na literatura.. O Freeman tem uma se&amp;#xE7;&amp;#xE3;o no seu livro chamado &amp;#x201C;Listening to the Tests&amp;#x201D;.
  • #31 God Class: classe com alto acoplamento, que conhece ou faz muito.
  • #32 veja que a classe CounterPartyFinder &amp;#xE9; usada apenas pela MessageUnpacker... Pq n&amp;#xE3;o passar a depend&amp;#xEA;ncia pra ela?
  • #33 Agora podemos ver que essa regra de neg&amp;#xF3;cio pode ser encapsulada dentro de outro objeto menor e mais espec&amp;#xED;fico, com um nome que fa&amp;#xE7;a sentido ao dom&amp;#xED;nio...
  • #38 - Voc&amp;#xEA; pode definir valores padr&amp;#xF5;es e permitir que os mesmos sejam alterados depois. - Receba apenas o que for realmente depend&amp;#xEA;ncia!
  • #40 veja a API do asp.net mvc! Precisa mockar muita coisa para testar o HttpRequest
  • #41 ao fazer isso, vc faz com que A de maneira impl&amp;#xED;cita conhe&amp;#xE7;a os detalhes internos de B, aumentando o acoplamento.
  • #42 Se S &amp;#xE9; um subtipo de T, ent&amp;#xE3;o objetos do tipo T podem ser substitu&amp;#xED;dos por S sem alterar nenhuma propriedade do sistema. - Pr&amp;#xE9;-condi&amp;#xE7;&amp;#xF5;es iguais ou mais fracas e p&amp;#xF3;s-condi&amp;#xE7;&amp;#xF5;es iguais ou mais fortes. Exemplo do quadrado, retangulo
  • #43 parecem notifiers, n&amp;#xE3;o?
  • #44 a classe faz mais do que devia! baixa coes&amp;#xE3;o! como comentado anteriormente, esse &amp;#xE9; um bom exemplo de depend&amp;#xEA;ncias do tipo &amp;#x201C;notifiers&amp;#x201D;, e pode ser refatorado!
  • #45 refatorado para observer... agora quem quiser ser notificado sobre a geracao da nota, torna-se um observer, e ser&amp;#xE1; notificado assim que a nota for gerada... isso desacopla o GeradorDeNotaFiscal do resto do processo, e permite que evolua de maneira mais f&amp;#xE1;cil!
  • #46 e conhecimento de OO &amp;#xE9; fundamental...
  • #48 voc&amp;#xEA; deve na medida do poss&amp;#xED;vel optar por pedir ao objeto para fazer, ao inv&amp;#xE9;s de perguntar algo, e a partir da resposta, tomar uma a&amp;#xE7;&amp;#xE3;o. O problema &amp;#xE9; que a classe que faz isso n&amp;#xE3;o deve tomar decis&amp;#xF5;es baseado no estado do outro objeto, violando o encapsulamento.
  • #49 dados e comportamento devem andar juntos... Muitas vezes abrimos m&amp;#xE3;o disso para flexibilizar (e muitos DP se baseiam nisso). Mas deve-se olhar atentamente!
  • #50 imagina agora para escrever todos os testes.. era complicado! precis&amp;#xE1;vamos extrair o comportamento para algum lugar... - outro exemplo: criar filtros... se tal campo foi postado, ent&amp;#xE3;o adicione isso na condi&amp;#xE7;&amp;#xE3;o, e por a&amp;#xED; vai...
  • #51 crie classes especializadas que fa&amp;#xE7;am pequenas partes do todo, cada uma com um comportamento bem espec&amp;#xED;fico. Fa&amp;#xE7;a depois com que seja f&amp;#xE1;cil junt&amp;#xE1;-las para formar um comportamento maior.
  • #52 a primeira refatoracao eh levar cada filtro para uma classe especifica (depois pode ter uma factory para montar os filtros)... veja que ficou facil customizar o comportamento de um filtro, al&amp;#xE9;m de ser muito mais f&amp;#xE1;cil testar: agora o n&amp;#xFA;mero de combina&amp;#xE7;&amp;#xF5;es para cada um deles &amp;#xE9; muito menor!
  • #53 o teste vai falhar sempre, pois a data setada vai ser diferente da data comparada, j&amp;#xE1; que a implementa&amp;#xE7;&amp;#xE3;o usa DateTime.Now!
  • #54 novamente, encontre a melhor abstra&amp;#xE7;&amp;#xE3;o para o seu problema... Encapsule isso em uma classe de dom&amp;#xED;nio. Parece que n&amp;#xE3;o, mas muitos sistemas que lidam com data s&amp;#xE3;o dif&amp;#xED;ceis de testar pois lidam com data diretamente.
  • #55 agora voc&amp;#xEA; consegue mockar o comportamento da data! alem disso, voc&amp;#xEA; tem uma interface muito mais rica para o seu dom&amp;#xED;nio que trata de datas! Com isso, voc&amp;#xEA; pode adicionar novos comportamentos de maneira f&amp;#xE1;cil!
  • #56 isso nos mostra que devemos sempre evitar singletons! comportamentos em singletons dificulta os testes dos clientes que usam, al&amp;#xE9;m de criar uma depend&amp;#xEA;ncia forte.
  • #57 os nomes devem ser claros! se os nomes n&amp;#xE3;o s&amp;#xE3;o claros o bastante, pode indicar que a abstra&amp;#xE7;&amp;#xE3;o n&amp;#xE3;o foi bem pensada!
  • #58 classes que tem o sufixo Impl indicam um problema de design. Geralmente isso acontece pq a classe tem apenas uma &amp;#xFA;nica implementa&amp;#xE7;&amp;#xE3;o! Por exemplo, se vc tiver a interface Cliente, e a classe ClienteImpl, muito provavelmente vc tem um problema na abstra&amp;#xE7;&amp;#xE3;o!
  • #59 m&amp;#xE9;todos com &amp;#x201C;E&amp;#x201D; no nome j&amp;#xE1; indicam que o m&amp;#xE9;todo faz duas coisas!
  • #60 repare que a diferen&amp;#xE7;a &amp;#xE9; apenas o tipo do imposto
  • #62 - eles apenas aumentam a complexidade ciclom&amp;#xE1;tica do c&amp;#xF3;digo - pode se tornar gigante, e a manuten&amp;#xE7;&amp;#xE3;o pode ser complicada
  • #63 strategy, state ajudam a reduzir essa complexidade!
  • #64 idealmente n&amp;#xE3;o se tem muito para testar em uma classe... ela deve ter apenas uma responsabilidade! se isso acontecer, voc&amp;#xEA; muito provavelmente est&amp;#xE1; testando uma brain class!
  • #65 pode indicar que essa classe tambem toma muita decisao sozinha!
  • #66 se vc acha q precisa testar um m&amp;#xE9;todo privado, &amp;#xE9; pq essa classe est&amp;#xE1; fazendo coisa demais! :)
  • #67 o livro do feathers d&amp;#xE1; algumas sugest&amp;#xF5;es de como fazer essa refatora&amp;#xE7;&amp;#xE3;o!