Test-Driven Development e sua influência no design

2,437 views

Published on

Minha palestra sobre TDD e sua influência no design, dada no QCON SP de 2010.

0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,437
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
Downloads
82
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide
  • meu nome eh ..
    aluno de mestrado, onde pesquiso sobre tdd
    trablho na locaweb

  • - Famoso ciclo
    - Adicione um teste; Rode todos os testes e veja o novo teste falhar; Faça uma pequena alteração; Rode todos os testes e veja todos passarem; Refatore para remover duplicação.
  • tem teste no nome...

  • - Transforma o teste em uma atividade de design, onde os programadores os utilizam para esclarecer as expectativas sobre o que um pedaço de código deve fazer; mais do que isso, faz vc pensar também no design, e poder mudar de idéia sobre ele (feedback);
    - Ajuda a resolver um dos maiores problemas do desenvolvimento de software, que é o gerenciamento de dependências
  • ter “test” no nome confunde!
    muitas definições que só levam em conta o fato de vc escrever um teste antes (igual a do JUnit in action)

  • agora todo mundo já sabe... em todo lugar, você encontra pessoas falando que TDD não é sobre testes e sim sobre design!
  • - outra coisa que está 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
  • - sinergia grande entre código fácil de testar e bom design
    - como vimos no exemplo acima, TDD faz com que as dependências fiquem naturalmente explícitas
    - além disso, faz com que os comportamentos sejam invocados de forma mais conveniente.
  • seus testes provem feedback para vc... vc pensa nas dependencias que essa classe tem, na interface que ela proverá para seus clientes e etc...
  • quando os testes passam a ficar complicados, sempre há uma refatoração para fazer!
  • novas classes emergem a partir do feedback dos testes!
    a composicao eh mais simples que a soma das partes
  • pensar em um design um pouco mais avancado nao eh facil.. como falei, vai da experiencia do programador...
  • não é tão fácil assim usar o feedback dos testes para guiar o desenvolvimento...
  • a primeira vez que vc faz TDD, vc é meio que obrigado a deixar as dependencias explícitas.. e é isso que vc faz...








  • e quando você vê , você tem uma classe com MUITAS dependências, o que faz com que a dificulta a manutenção da mesma.

  • os testes provêm um feedback imenso sobre o seu design!
  • não há muito assunto sobre isso na literatura.. O Freeman tem uma seção no seu livro chamado “Listening to the Tests”.
  • God Class: classe com alto acoplamento, que conhece ou faz muito.
  • veja que a classe CounterPartyFinder é usada apenas pela MessageUnpacker... Pq não passar a dependência pra ela?
  • Agora podemos ver que essa regra de negócio pode ser encapsulada dentro de outro objeto menor e mais específico, com um nome que faça sentido ao domínio...




  • - Você pode definir valores padrões e permitir que os mesmos sejam alterados depois.
    - Receba apenas o que for realmente dependência!

  • veja a API do asp.net mvc! Precisa mockar muita coisa para testar o HttpRequest
  • ao fazer isso, vc faz com que A de maneira implícita conheça os detalhes internos de B, aumentando o acoplamento.
  • Se S é um subtipo de T, então objetos do tipo T podem ser substituídos por S sem alterar nenhuma propriedade do sistema.
    - Pré-condições iguais ou mais fracas e pós-condições iguais ou mais fortes. Exemplo do quadrado, retangulo
  • parecem notifiers, não?
  • a classe faz mais do que devia! baixa coesão!
    como comentado anteriormente, esse é um bom exemplo de dependências do tipo “notifiers”, e pode ser refatorado!
  • refatorado para observer... agora quem quiser ser notificado sobre a geracao da nota, torna-se um observer, e será notificado assim que a nota for gerada... isso desacopla o GeradorDeNotaFiscal do resto do processo, e permite que evolua de maneira mais fácil!
  • e conhecimento de OO é fundamental...

  • você deve na medida do possível optar por pedir ao objeto para fazer, ao invés de perguntar algo, e a partir da resposta, tomar uma ação. O problema é que a classe que faz isso não deve tomar decisões baseado no estado do outro objeto, violando o encapsulamento.
  • dados e comportamento devem andar juntos... Muitas vezes abrimos mão disso para flexibilizar (e muitos DP se baseiam nisso). Mas deve-se olhar atentamente!
  • imagina agora para escrever todos os testes.. era complicado! precisávamos extrair o comportamento para algum lugar...
    - outro exemplo: criar filtros... se tal campo foi postado, então adicione isso na condição, e por aí vai...
  • crie classes especializadas que façam pequenas partes do todo, cada uma com um comportamento bem específico. Faça depois com que seja fácil juntá-las para formar um comportamento maior.
  • 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ém de ser muito mais fácil testar: agora o número de combinações para cada um deles é muito menor!
  • o teste vai falhar sempre, pois a data setada vai ser diferente da data comparada, já que a implementação usa DateTime.Now!
  • novamente, encontre a melhor abstração para o seu problema... Encapsule isso em uma classe de domínio. Parece que não, mas muitos sistemas que lidam com data são difíceis de testar pois lidam com data diretamente.
  • agora você consegue mockar o comportamento da data!
    alem disso, você tem uma interface muito mais rica para o seu domínio que trata de datas! Com isso, você pode adicionar novos comportamentos de maneira fácil!
  • isso nos mostra que devemos sempre evitar singletons! comportamentos em singletons dificulta os testes dos clientes que usam, além de criar uma dependência forte.
  • os nomes devem ser claros! se os nomes não são claros o bastante, pode indicar que a abstração não foi bem pensada!
  • classes que tem o sufixo Impl indicam um problema de design. Geralmente isso acontece pq a classe tem apenas uma única implementação! Por exemplo, se vc tiver a interface Cliente, e a classe ClienteImpl, muito provavelmente vc tem um problema na abstração!
  • métodos com “E” no nome já indicam que o método faz duas coisas!
  • repare que a diferença é apenas o tipo do imposto

  • - eles apenas aumentam a complexidade ciclomática do código
    - pode se tornar gigante, e a manutenção pode ser complicada
  • strategy, state ajudam a reduzir essa complexidade!
  • idealmente não se tem muito para testar em uma classe... ela deve ter apenas uma responsabilidade! se isso acontecer, você muito provavelmente está testando uma brain class!
  • pode indicar que essa classe tambem toma muita decisao sozinha!
  • se vc acha q precisa testar um método privado, é pq essa classe está fazendo coisa demais! :)
  • o livro do feathers dá algumas sugestões de como fazer essa refatoração!






  • Test-Driven Development e sua influência no design

    1. 1. Test-Driven Development e sua Influência no Design Mauricio Aniche http://www.aniche.com.br
    2. 2. Mas o que é TDD mesmo?
    3. 3. Red - Green - Refactor
    4. 4. É sobre testes?
    5. 5. NÃO!
    6. 6. É sobre design!
    7. 7. Mas que confusão!
    8. 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. 9. mas todo mundo já sabe que tdd é sobre design!
    10. 10. TDD mostra o problema, mas não resolve pra você!
    11. 11. E como ele ajuda?
    12. 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. 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. 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. 15. mas não é fácil!
    16. 16. na primeira vez...
    17. 17. public class Carro { public Carro () { } }
    18. 18. public class Carro { private Pneus pneuDianteiro; public Carro (Pneus pneuDianteiro) { this.pneuDianteiro = pneuDianteiro; } }
    19. 19. public class Carro { private Pneus pneuDianteiro; private Pneus pneuTraseiro; public Carro (Pneus pneuDianteiro, Pneus pneuTraseiro) { this.pneuDianteiro = pneuDianteiro; this.pneuTraseiro = pneuTraseiro; } }
    20. 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. 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. 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. 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. 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. 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. 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. 27. Mas quando você aprende a usar...
    28. 28. Ouça seus testes!
    29. 29. Vou mostrar como eu vejo o feedback dos testes.
    30. 30. 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.. } }
    31. 31. 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()); } } }
    32. 32. 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()); } } }
    33. 33. 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); } }
    34. 34. mas e o exemplo do carro?
    35. 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. 36. dependencies, notifications, adjustments
    37. 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. 38. use um builder! new CarroBuilder().ComPneu(Pneus.Firestone).ComMotor(Motor.Zetec).[...].Cria(); by @cv
    39. 39. 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));         }     }
    40. 40. Lei de Demeter [nada de A.B.metodoC() ...]
    41. 41. Cuidado com herança Lembre-se do Princípio de Substituição de Liskov (LSP)
    42. 42. 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()); }}
    43. 43. Single Responsibility Principle (SRP) (A classe deve ter apenas uma responsabilidade)
    44. 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. 45. O teste mostra, mas quem refatora (para padrões) é você!
    46. 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. 47. Tell, Don’t Ask! (Você deve dizer ao objeto o que quer que ele faça!)
    48. 48. 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); ... }
    49. 49. 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(); } ...
    50. 50. Crie classes especializadas!
    51. 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. 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. 53. Abstração é o segredo!
    54. 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. 55. Evite singletons!
    56. 56. 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(); }
    57. 57. Impl classes are meaningless!
    58. 58. Métodos FazXEY()
    59. 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. 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. 61. Elimine if’s e switches na medida do possível!
    62. 62. Padrões de projeto podem ajudar (de novo)!
    63. 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. 64. A mesma coisa se você tem um cenário muito grande para um simples teste!
    65. 65. A mesma coisa se você achar que precisa testar um método privado!
    66. 66. Quebre essa classe em pequenas outras classes! (lembre do SRP novamente!)
    67. 67. Mas é assim que eu interpreto!
    68. 68. Você pode achar outras maneiras de interpretar! e se achar, me conta! ;)
    69. 69. "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"
    70. 70. Ouça seus testes! deu pra ver o quanto eles têm pra contar?
    71. 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. 72. Obrigado! @mauricioaniche

    ×