TDD - Algumas lições aprendidas com o livro GOOS

Embed Size (px)

DESCRIPTION

- TDD: Red – Green – Refactor - Testes de Unidade x Testes de Integração - Ports and Adapters Pattern - Business Domain versus Technical Domain - Testes de Unidade com JMock - Coverage Tests - Logging Tests

Citation preview

  • 1. Test Driven Development
    • Fbio Luiz Nery de Miranda baseado no livroGrowing OO Software Guided by Tests

2. Roadmap

  • Problema: autenticao em um service provider de terceiros.

3. TDD:RedGreenRefactor 4. Testes de Unidadex Testes de Integrao 5. Ports and Adapters Pattern

  • (Business Domain versus Technical Domain)

Testes de Unidade com JMock 6. Coverage Tests 7. Logging Tests 8. Autenticao num Service Provider de Terceiros

  • API REST / XML

POST http://isp-authenticator.dev.globoi.com:8280/ws/rest/autenticacao XML Resquest Autenticao: 303a6a6f616f646576406d61696cfulano123456127.0.0.1 XML Response Autenticao [email protected]@spam.laAUTENTICADOATIVONAO_ASSINANTE21588993 9. Por onde comear?

  • Defina a classe de domnio que deseja testar (System Under Test SUT)

10. Queremos uma classe que realize autenticao (um Authenticator) 11. O ponto de partida ser AuthenticatorTests 12. Qual o foco do teste? O nome do teste expressa a inteno?

  • Defina o foco do teste, criando um mtodo de teste cujo nome expresse a inteno
  • (Ajuda a no se afastar do foco)

Um Authenticator deve realizar autenticao caso o nome do usurio e a senha estejam corretos. 13. @Test 14. public void shouldAuthenticateWhenCredentialsAreValid() { 15. } 16. Exercitando o teste

  • Comece a implementar o teste exercitando o SUT (mesmo que o SUT ainda no exista)

17. @Test 18. public void shouldAuthenticateWhenCredentialsAreValid() {

    • AuthenticationResponse response = authenticator.authenticate(validUsername, validPassword);

} 19. Setup e Assertions

  • Faa as asseres e o setup necessrios.

20. private final Authenticator authenticator = new Authenticator(); 21. private final String validUsername = [email protected]; 22. private final String validUsername = va1idpassw0rd; 23. @Test 24. public void shouldAuthenticateWhenCredentialsAreValid() {

    • AuthenticationResponse response = authenticator.authenticate(validUsername, validPassword);
  • 25. assertThat(authentication response, response, is(notNullValue()));

26. assertThat(authentication response, response.isAuthenticated(), is(true)); } 27. Implemente as classes do domnio (stubs vazios)

  • Crie as classes, mas apenas o suficiente para desaparecerem os erros de compilao.

28. Observe o testefalhar 29. A mensagem de falha do teste expressa suficientemente bem o que est quebrado? 30. A importncia de falhar antes

  • Evitar falsos positivos

31. Promove boas prticas dediagnstico de falhas (GOOS Best Practice!)

  • A clareza nas mensagens de falha muito importante

32. Elas ajudaro a diagnosticar problemas, quando novas features provoquem quebra de cdigo existente. 33. A ausncia de mensagens claras sobre a falha forar o desenvolvedor a ter que ler e entender a implementao para saber o que quebrou (pior caso: quando o dev que fez quebrar o cdigo no o prprio autor do cdigo) Hamcrest Library: matchers so synthax sugar e ajudam a criar mensagens de falha mais detalhadas. 34. Implemente o cdigomais simplesque permita o testePASSAR

  • No precisa implementar toda a lgica complexa de uma s vez

35. KISS Keep It Simple 36. No perca o foco do teste. 37. No se distraia com premissas que devem ser validadas em outros testes. 38. DONE?

  • O que o Authenticator deveria fazer?
  • POST num servio REST/XML

O que o Authenticator est fazendo? publicAuthenticationResponse authenticate(String username, String password)throwsAuthenticationFailure { return newAuthenticationResponse();// lazy boy... } public classAuthenticationResponse { publicBoolean isAuthenticated() { return true ;// aham... } }

  • Authenticator um fanfarro...

39. Pausa para Reflexo

  • Authenticator deve se preocupar com os detalhes de como a autenticao realizada?

40. Resposta: Depende (nenhuma verdade absoluta) 41. Se est interessado em criar um Domain Model, importante separar classes de domnio das classes que possuem detalhes tcnicos de como a funcionalidade implementada. 42. Ports and Adapters Pattern

  • Ports: interfaces p/ abstrair relaes domnio x dependncias externas)

43. Adapters: implementam as interfaces, encapsulando o domnio tcnico da aplicao 44. Testes de Unidade: verificam o comportamento do Authenticator. 45. Testes de Integrao: verificam a implementao do delegate. 46. Ports and Adapters Pattern (GOOS) 47. Retornando foco ao teste...

  • Queremos saber se o Authenticator comanda corretamente o delegate, sem se preocupar em como o delegate ser implementado.

privateMockerycontext=newMockery(); private finalAuthenticator.Delegatedelegate=context .mock(Authenticator.Delegate. class ); privateAuthenticatorauthenticator=newAuthenticator( delegate ); @Test public voidshouldAuthenticateSucessfully()throwsException { context .checking( newExpectations() {{ AuthenticationResponse sucessfullAuthentication =newAuthenticationResponse(); sucessfullAuthentication.setAuthenticated( true ); oneOf( delegate ).performAuthentication(with( validUsername ), with( validPassword )); will( returnValue (sucessfullAuthentication)); }}); AuthenticationResponse response =authenticator .authenticate( validUsername ,validPassword ); assertThat ( "authentication response" , response,is ( notNullValue ())); assertThat ( "authentication response" , response.isAuthenticated(),is ( true )); } 48. Rodando os Testes...

  • Pegadinha do Malandro: @RunWith(JMock.class), para o Jmock verificar se os Mock Objects do contexto foram executados de acordo com as expectativas.

49. Falhar o teste : o teste falha de forma clara, facilitando o correto diagnstico? 50. Corrigir Authenticator e AuthenticationResponse epassar 51. DONE?

  • A classe Authenticator est coberta por testes de unidade.

52. O Delegate responsvel pela autenticao via POST/XML injetado no Authenticator, via construtor. 53. Resta prover uma implementao do Delegate um RestTemplateDelegate, por exemplo e escrever um teste de integrao. 54. Teste de Integrao @RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations= "classpath:beans.xml" ) public classRestTemplateAuthenticatorTests { @Autowired privateRestTemplateAuthenticationDelegaterestTemplateDelegate ; privateAuthenticatorauthenticator ; @Before public voidsetup() { authenticator=newAuthenticator( restTemplateDelegate ); } @Test public voidauthenticateUser()throwsException { assertTrue ( authenticator .authenticate( "[email protected]" ,"teste123456" ).isAuthenticated()); } 55. RestTemplateAutenticationDelegate public classRestTemplateAuthenticationDelegateimplementsAuthenticator.Delegate { private finalStringurl ; private finalRestTemplaterest ; publicRestTemplateAuthenticationDelegate(String url, RestTemplate rest) { this . url= url; this . rest= rest; } publicAuthenticationResponse performAuthentication(String username, String password)throwsAuthenticationFailure { try{ XmlAuthenticationRequest xmlRequest =newXmlAuthenticationRequest(); xmlRequest.setIp( "127.0.0.1" ); xmlRequest.setLogin(username); xmlRequest.setSenha(password); XmlAuthenticationResponse xmlResponse =rest .postForObject( url , xmlRequest, XmlAuthenticationResponse. class ); AuthenticationResponse response =newAuthenticationResponse(); response.setAuthenticated(xmlResponse.getStatus().equals( "AUTENTICADO" )); returnresponse; }catch(Throwable e) { throw newAuthenticationFailure(e); } } } 56. E depois?

  • Refactor Clean Code

57. Coverage Test (Eclipse Emma) 58. Manter a cobertura dos testes em nvel elevado (acima de 90%) praticamente impossvel se voc no pratica Test First. 59. Aumentar a cobertura de cdigo Test After muito mais difcil, e pode dar um falsa sensao de cobertura. 60. Se der tempo...

  • shouldNotAuthenticateInvalidUsername()
  • RedGreenRefactor Coverage

ShouldNotAuthenticateInvalidPassword()

  • RedGreenRefactor Coverage

shouldLogFailureOnAuthenticationErrors() 61. LoggingTests! 62. Logging Tests

  • Como normalmente fazemos logging:

private static finalLoggerlogger= Logger.getLogger(Authenticator. class ); ... if( logger .isDebugEnabled()) logger .debug(message);

  • Logging is a Feature: logging a User Interface do time de suporte!

63. Deve-se dar tanta importncia a testes de Logging como damos a testes de UI! 64. No desejvel que as mensagens crticas de logging quebrem com a evoluo da aplicao 65. Caso existam ferramentas automticas de anlise de log, elas iro quebrar caso o logging no seja estvel com o passar do tempo. 66. Mensagens de debbuging no precisam ser testadas. 67. Deve-se selecionar com critrio as mensagens crticas para a aplicao, que precisam ser testadas. 68. Tratar como Logging como Requisitos de Suporte? 69. Logging: Business Domain x Technical Domain

  • possvel testar o arquivo de logging e verificar se as mensagens esperadas esto presentes.

70. Amarra a implementao biblioteca de Logging (mistura o domnio do negcio com o domnio tcnico). 71. Ports and Adapters strikes back

  • Sugesto: Ports and Adapters Pattern.
  • Authenticator interage com um delegate FailureReporter.

72. Testes de unidade verificam a relao entre Authenticator e FailureReporter 73. Testes de Integrao verificam a implementao do FailureReporter. 74. Logging com Ports e Adapters

  • Bonus: permite a implementao de diferentes estratgias de FailureReporting
  • Envio de Email, Persistncia / Estatsticas de falha, etc...

75. Dvidas?

  • Obrigado!