Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TDD Quest

David
July 05, 2012

TDD Quest

Vous subissez les régressions à chaque livraison ? Vous ne voyez pas l’intéret des tests unitaires car ils ne servent qu’à tester des additions ? Les tests ne sont pas applicables à votre projet car il est trop complexe ? Si c’est le cas, suivez moi dans la quête du Test Driven Development. Nous rencontrerons pléthore d'ennemis contre lesquels nous aurons à combattre : bugs, complexité, code statique, couplage fort. Ils essaieront de nous barrer la route, mais heureusement, nous pourrons compter sur nos alliés jUnit, Mockito, refactoring et injection/dépendance.

David

July 05, 2012
Tweet

More Decks by David

Other Decks in Programming

Transcript

  1. Test unitaire ? Mock ? TDD ? Kezako ? en

    finir avec les régressions par David Wursteisen 15 JUIN 2012
  2. Test unitaire ? Mock ? TDD ? Kezako ? en

    finir avec les régressions par David Wursteisen 15 JUIN 2012
  3. La documentation explique le code /** * * @param xml

    : document xml représentant le swap * @return objet Swap */ public Swap parse(String xml) { Swap swap = new Swap(); String currency = getNodeValue("/swap/currency", xml); swap.setCurrency(currency); /* beaucoup de code... */ Date d = new Date(); if(test == 1) { if(d.after(spotDate)) { swap.setType("IRS"); } else { swap.setType("CURVE"); } } else if (test == 2) { if(d.after(spotDate)) { swap.setType("IRS"); } else { swap.setType("CURVE"); } } /* encore beaucoup de code... */ return swap; }
  4. La documentation explique le code 1 /** 2 * 3

    * @param xml : document xml représentant le swap 4 * @return objet Swap 5 */ 6 public Swap parse(String xml) { 7 Swap swap = new Swap(); 8 String currency = getNodeValue("/swap/currency", xml); 9 swap.setCurrency(currency); [...] /* beaucoup de code... */ 530 Date d = new Date(); 531 if(test == 1) { 532 if(d.after(spotDate)) { 533 swap.setType("IRS"); 534 } else { 535 swap.setType("CURVE"); 536 } 537 } else if (test == 2) { 538 if(d.after(spotDate)) { 539 swap.setType("IRS"); 540 } else { 541 swap.setType("CURVE"); 542 } 543 } [...] /* encore beaucoup de code... */ 1135 return swap; 1136 } 1000 LIGNES !
  5. La documentation a raison 1 /** 2 * Always returns

    true. 3 */ 4 public boolean isAvailable() { 5 return false; 6 }
  6. La documentation a raison 1 /** 2 * Always returns

    true. 3 */ 4 public boolean isAvailable() { 5 return false; 6 } WTF ?!?
  7. Faire du code qui marche 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 }
  8. Faire du code qui marche 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 }
  9. Faire du code qui marche 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 } NullPointerException !
  10. Code simple 1 public void affiche(BankAccount account, ProductService service) {

    2 System.out.println("Bank Account : "+this.name); 3 System.out.println("Autres informations : "); 4 account = (BankAccount) service.getProduct(this.name); 5 account.deposit(); 6 System.out.println(account); 7 System.out.println("=== FIN ==="); 8 9 }
  11. Code simple 1 public void affiche(BankAccount account, ProductService service) {

    2 System.out.println("Bank Account : "+this.name); 3 System.out.println("Autres informations : "); 4 account = (BankAccount) service.getProduct(this.name); 5 account.deposit(); 6 System.out.println(account); 7 System.out.println("=== FIN ==="); 8 9 }
  12. Test d’abord ! 1 @Test 2 public void can_deposit() {

    3 bankAccount.setBalance(50); 4 5 boolean result = bankAccount.deposit(1000); 6 7 assertTrue(result); 8 assertEquals(1050, bankAccount.getBalance()); 9 }
  13. Test d’abord ! 1 boolean deposit(int amount) { 2 bal

    = bal + amount; 3 return true; 4 }
  14. Test d’abord ! 1 boolean deposit(int amount) { 2 bal

    = bal + amount; 3 return true; 4 }
  15. Test d’abord ! 1 @Test 2 public void cant_deposit_with_negative_amount() {

    3 bankAccount.setBalance(100); 4 5 boolean result = bankAccount.deposit(-20); 6 7 assertFalse(result); 8 assertEquals(100, bankAccount.getBalance()); 9 }
  16. Test d’abord ! 1 boolean deposit(int amount) { 2 bal

    = bal + amount; 3 return true; 4 }
  17. Test d’abord ! 1 boolean deposit(int amount) { 2 bal

    = bal + amount; 3 return true; 4 }
  18. Test d’abord ! 1 boolean deposit(int amount) { 2 if(amount

    < 0) { 3 return false; 4 } 5 bal = bal + amount; 6 return true; 7 }
  19. Test d’abord ! 1 boolean deposit(int amount) { 2 if(amount

    < 0) { 3 return false; 4 } 5 bal = bal + amount; 6 return true; 7 }
  20. les «3 A» : Acteur/Action/Assertion 1 @Test 2 public void

    cant_deposit_with_negative_amount() { 3 bankAccount.setBalance(100); 4 5 boolean result = bankAccount.deposit(-20); 6 7 assertFalse(result); 8 assertEquals(100, bankAccount.getBalance()); 9 } Acteur
  21. les «3 A» : Acteur/Action/Assertion 1 @Test 2 public void

    cant_deposit_with_negative_amount() { 3 bankAccount.setBalance(100); 4 5 boolean result = bankAccount.deposit(-20); 6 7 assertFalse(result); 8 assertEquals(100, bankAccount.getBalance()); 9 } Action
  22. les «3 A» : Acteur/Action/Assertion 1 @Test 2 public void

    cant_deposit_with_negative_amount() { 3 bankAccount.setBalance(100); 4 5 boolean result = bankAccount.deposit(-20); 6 7 assertFalse(result); 8 assertEquals(100, bankAccount.getBalance()); 9 } Assertions
  23. IT

  24. 1 @Test 2 public void testGetCustomerOK() { 3 4 LOGGER.info("=======

    testGetCustomerOK starting..."); 5 6 try { 7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99"); 8 9 // Vérifier. 10 Assert.assertNotNull("Extra non trouvé.", targetDTO); 11 Assert.assertEquals("Les accountId doivent être identiques.", 12 "ABC99", targetDTO.getAccountId()); 13 14 } catch (CustomerBusinessException exception) { 15 LOGGER.error("CustomerBusinessException : {}", 16 exception.getCause()); 17 Assert.fail(exception.getMessage()); 18 } catch (UnavailableResourceException exception) { 19 LOGGER.error("UnavailableResourceException : {}", 20 exception.getMessage()); 21 Assert.fail(exception.getMessage()); 22 } catch (UnexpectedException exception) { 23 LOGGER.error("UnexpectedException : {}" + 24 exception.getMessage()); 25 Assert.fail(exception.getMessage()); 26 } catch (Exception exception) { 27 LOGGER.error("CRASH : " + exception.getMessage()); 28 Assert.fail(exception.getMessage()); 29 } 30 31 LOGGER.info("======= testGetCustomerOK done."); 32 }
  25. 1 @Test 2 public void testGetCustomerOK() { 3 4 LOGGER.info("=======

    testGetCustomerOK starting..."); 5 6 try { 7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99"); 8 9 // Vérifier. 10 Assert.assertNotNull("Extra non trouvé.", targetDTO); 11 Assert.assertEquals("Les accountId doivent être identiques.", 12 "ABC99", targetDTO.getAccountId()); 13 14 } catch (CustomerBusinessException exception) { 15 LOGGER.error("CustomerBusinessException : {}", 16 exception.getCause()); 17 Assert.fail(exception.getMessage()); 18 } catch (UnavailableResourceException exception) { 19 LOGGER.error("UnavailableResourceException : {}", 20 exception.getMessage()); 21 Assert.fail(exception.getMessage()); 22 } catch (UnexpectedException exception) { 23 LOGGER.error("UnexpectedException : {}" + 24 exception.getMessage()); 25 Assert.fail(exception.getMessage()); 26 } catch (Exception exception) { 27 LOGGER.error("CRASH : " + exception.getMessage()); 28 Assert.fail(exception.getMessage()); 29 } 30 31 LOGGER.info("======= testGetCustomerOK done."); 32 }
  26. 1 @Test 2 public void testGetCustomerOK() { 3 4 LOGGER.info("=======

    testGetCustomerOK starting..."); 5 6 try { 7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99"); 8 9 // Vérifier. 10 Assert.assertNotNull("Extra non trouvé.", targetDTO); 11 Assert.assertEquals("Les accountId doivent être identiques.", 12 "ABC99", targetDTO.getAccountId()); 13 14 } catch (CustomerBusinessException exception) { 15 LOGGER.error("CustomerBusinessException : {}", 16 exception.getCause()); 17 Assert.fail(exception.getMessage()); 18 } catch (UnavailableResourceException exception) { 19 LOGGER.error("UnavailableResourceException : {}", 20 exception.getMessage()); 21 Assert.fail(exception.getMessage()); 22 } catch (UnexpectedException exception) { 23 LOGGER.error("UnexpectedException : {}" + 24 exception.getMessage()); 25 Assert.fail(exception.getMessage()); 26 } catch (Exception exception) { 27 LOGGER.error("CRASH : " + exception.getMessage()); 28 Assert.fail(exception.getMessage()); 29 } 30 31 LOGGER.info("======= testGetCustomerOK done."); 32 } BRUIT
  27. KISS ! 1 @Test 2 public void can_get_customer() throws Exception

    { 3 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99"); 4 Assert.assertEquals("Les accountId doivent être identiques.", 5 "ABC99", targetDTO.getAccountId()); 6 }
  28. Artillerie framework de test mock java c# php python junit

    + fest assert mockito NUnit moq php unit php unit unittest Mock
  29. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 }
  30. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 } LE BUG EST ICI !
  31. nouveau bug = nouveau test 1 @Test 2 public void

    can_return_null_when_ioexception() throws IOException { 3 Mockito.doThrow(IOException.class) 4 .when(daoService).getObject("product", "azerty"); 5 6 Object product = service.getProduct("azerty"); 7 8 assertThat(product).isNull(); 9 }
  32. nouveau bug = nouveau test 1 @Test 2 public void

    can_return_null_when_ioexception() throws IOException { 3 Mockito.doThrow(IOException.class) 4 .when(daoService).getObject("product", "azerty"); 5 6 Object product = service.getProduct("azerty"); 7 8 assertThat(product).isNull(); 9 }
  33. nouveau bug = nouveau test 1 @Test 2 public void

    can_return_null_when_ioexception() throws IOException { 3 Mockito.doThrow(IOException.class) 4 .when(daoService).getObject("product", "azerty"); 5 6 Object product = service.getProduct("azerty"); 7 8 assertThat(product).isNull(); 9 }
  34. nouveau bug = nouveau test 1 @Test 2 public void

    can_return_null_when_ioexception() throws IOException { 3 Mockito.doThrow(IOException.class) 4 .when(daoService).getObject("product", "azerty"); 5 6 Object product = service.getProduct("azerty"); 7 8 assertThat(product).isNull(); 9 }
  35. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 } LE BUG EST ICI !
  36. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj : " + obj.toString()); 7 } 8 return obj; 9 } LE BUG EST TOUJOURS ICI !
  37. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj with id: " + id); 7 } 8 return obj; 9 }
  38. nouveau bug = nouveau test 1 public Object getProduct(String id)

    { 2 Object obj = null; 3 try { 4 obj = networkService.getObject("product", id); 5 } catch (IOException e) { 6 System.err.println("Error with obj with id: " + id); 7 } 8 return obj; 9 }
  39. code complexe 1 /** 2 * 3 * @param xml

    : document xml représentant le swap 4 * @return objet Swap 5 */ 6 public Swap parse(String xml) { 7 Swap swap = new Swap(); 8 String currency = getNodeValue("/swap/currency", xml); 9 swap.setCurrency(currency); [...] /* beaucoup de code... */ 530 Date d = new Date(); 531 if(test == 1) { 532 if(d.after(spotDate)) { 533 swap.setType("IRS"); 534 } else { 535 swap.setType("CURVE"); 536 } 537 } else if (test == 2) { 538 if(d.after(spotDate)) { 539 swap.setType("IRS"); 540 } else { 541 swap.setType("CURVE"); 542 } 543 } [...] /* encore beaucoup de code... */ 1135 return swap; 1136 }
  40. 1 @Test 2 public void can_parse_xml() throws Exception { 3

    String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 4 "<!--\n" + 5 "\t== Copyright (c) 2002-2005. All rights reserved.\n" + 6 "\t== Financial Products Markup Language is subject to the FpML public license.\n" + 7 "\t== A copy of this license is available at http://www.fpml.org/documents/license\n" + 8 "-->\n" + 9 "<FpML version=\"4-2\" xmlns=\"http://www.fpml.org/2005/FpML-4-2\" xmlns:xsi=\"http://www.w3.org/2001/ XMLSchema-instance\" xsi:schemaLocation=\"http://www.fpml.org/2005/FpML-4-2 fpml-main-4-2.xsd\" xsi:type= \"TradeCashflowsAsserted\">\n" + 10 "\t<header>\n" + 11 "\t\t<messageId messageIdScheme=\"http://www.example.com/messageId\">CEN/2004/01/05/15-38</messageId>\n" + 12 "\t\t<sentBy>ABC</sentBy>\n" + 13 "\t\t<sendTo>DEF</sendTo>\n" + 14 "\t\t<creationTimestamp>2005-01-05T15:38:00-00:00</creationTimestamp>\n" + 15 "\t</header>\n" + 16 "\t<asOfDate>2005-01-05T15:00:00-00:00</asOfDate>\n" + 17 "\t<tradeIdentifyingItems>\n" + 18 "\t\t<partyTradeIdentifier>\n" + 19 "\t\t\t<partyReference href=\"abc\"/>\n" + 20 "\t\t\t<tradeId tradeIdScheme=\"http://www.abc.com/tradeId/\">trade1abcxxx</tradeId>\n" + 21 "\t\t</partyTradeIdentifier>\n" + 22 "\t\t<partyTradeIdentifier>\n" + 23 "\t\t\t<partyReference href=\"def\"/>\n" + 24 "\t\t\t<tradeId tradeIdScheme=\"http://www.def.com/tradeId/\">123cds</tradeId>\n" + 25 "\t\t</partyTradeIdentifier>\n" + 26 "\t</tradeIdentifyingItems>\n" + 27 "\t<adjustedPaymentDate>2005-01-31</adjustedPaymentDate>\n" + 28 "\t<netPayment>\n" + 29 "\t\t<identifier netPaymentIdScheme=\"http://www.centralservice.com/netPaymentId\">netPaymentABCDEF001</ identifier>\n" + 30 "\t\t<payerPartyReference href=\"abc\"/>\n" + 31 "\t\t<receiverPartyReference href=\"def\"/>\n" + 32 "\t\t<paymentAmount>\n" +
  41. code complexe 1 /** 2 * 3 * @param xml

    : document xml représentant le swap 4 * @return objet Swap 5 */ 6 public Swap parse(String xml) { 7 Swap swap = new Swap(); 8 String currency = getNodeValue("/swap/currency", xml); 9 swap.setCurrency(currency); [...] /* beaucoup de code... */ 530 Date d = new Date(); 531 if(test == 1) { 532 if(d.after(spotDate)) { 533 swap.setType("IRS"); 534 } else { 535 swap.setType("CURVE"); 536 } 537 } else if (test == 2) { 538 if(d.after(spotDate)) { 539 swap.setType("IRS"); 540 } else { 541 swap.setType("CURVE"); 542 } 543 } [...] /* encore beaucoup de code... */ 1135 return swap; 1136 }
  42. code complexe 1 /** 2 * 3 * @param xml

    : document xml représentant le swap 4 * @return objet Swap 5 */ 6 public Swap parse(String xml) { 7 Swap swap = new Swap(); 8 String currency = getNodeValue("/swap/currency", xml); 9 swap.setCurrency(currency); [...] /* beaucoup de code... */ 530 Date d = new Date(); 531 if(test == 1) { 532 if(d.after(spotDate)) { 533 swap.setType("IRS"); 534 } else { 535 swap.setType("CURVE"); 536 } 537 } else if (test == 2) { 538 if(d.after(spotDate)) { 539 swap.setType("IRS"); 540 } else { 541 swap.setType("CURVE"); 542 } 543 } [...] /* encore beaucoup de code... */ 1135 return swap; 1136 } EXTRACT METHOD !
  43. code complexe 1 public void updateSwapType(Swap swapToUpdate, Date now, Date

    spotDate, int test) { 2 if(test == 1) { 3 if(now.after(spotDate)) { 4 swapToUpdate.setType("IRS"); 5 } else { 6 swapToUpdate.setType("CURVE"); 7 } 8 } else if (test == 2) { 9 if(now.after(spotDate)) { 10 swapToUpdate.setType("IRS"); 11 } else { 12 swapToUpdate.setType("CURVE"); 13 } 14 } 15 }
  44. code complexe 1 @Test 2 public void can_update_swap_type() throws Exception

    { 3 Swap swap = new Swap(); 4 Date now = simpleDateFormat.parse("2012-06-15"); 5 Date before = simpleDateFormat.parse("2012-05-05"); 6 service.updateSwapType(swap, now, before, 1); 7 assertEquals("IRS", swap.getType()); 8 }
  45. Singleton 1 public class ProductService { 2 3 private static

    ProductService instance = new ProductService(); 4 5 private DAOService daoService; 6 7 private ProductService() { 8 System.out.println("ProductService constructor"); 9 daoService = DAOService.getInstance(); 10 } 11 12 public static ProductService getInstance() { 13 return instance; 14 } 15 16 17 18 public Object getProduct(String id) { 19 return daoService.getObject("product", id); 20 } 21 }
  46. Singleton 1 public class ProductService { 2 3 private static

    ProductService instance = new ProductService(); 4 5 private DAOService daoService; 6 7 private ProductService() { 8 System.out.println("ProductService constructor"); 9 daoService = DAOService.getInstance(); 10 } 11 12 public static ProductService getInstance() { 13 return instance; 14 } 15 16 17 18 public Object getProduct(String id) { 19 return daoService.getObject("product", id); 20 } 21 }
  47. Singleton 1 public class ProductService { 2 3 private static

    ProductService instance = new ProductService(); 4 5 private DAOService daoService; 6 7 private ProductService() { 8 System.out.println("ProductService constructor"); 9 daoService = DAOService.getInstance(); 10 } 11 12 public static ProductService getInstance() { 13 return instance; 14 } 15 16 17 18 public Object getProduct(String id) { 19 return daoService.getObject("product", id); 20 } 21 }
  48. Singleton 1 public class ProductService { 2 3 private static

    ProductService instance = new ProductService(); 4 5 private DAOService daoService; 6 7 private ProductService() { 8 System.out.println("ProductService constructor"); 9 daoService = DAOService.getInstance(); 10 } 11 12 public static ProductService getInstance() { 13 return instance; 14 } 15 16 17 18 public Object getProduct(String id) { 19 return daoService.getObject("product", id); 20 } 21 }
  49. Singleton 1 public void validateSwap(String id) { 2 Swap swap

    = (Swap) ProductService.getInstance().getProduct(id); 3 swap.updateState("VALIDATE"); 4 ProductService.getInstance().save(swap); 5 }
  50. Singleton 1 public void validateSwap(String id) { 2 Swap swap

    = (Swap) ProductService.getInstance().getProduct(id); 3 swap.updateState("VALIDATE"); 4 ProductService.getInstance().save(swap); 5 } COUPLAGE FORT
  51. Injection 1 private ProductService productService; 2 3 public void setProductService(ProductService

    injectedService) { 4 this.productService = injectedService; 5 } 6 7 public void validateSwap(String id) { 8 Swap swap = (Swap) productService.getProduct(id); 9 swap.updateState("VALIDATE"); 10 productService.save(swap); 11 }
  52. Injection 1 private ProductService productService; 2 3 public void setProductService(ProductService

    injectedService) { 4 this.productService = injectedService; 5 } 6 7 public void validateSwap(String id) { 8 Swap swap = (Swap) productService.getProduct(id); 9 swap.updateState("VALIDATE"); 10 productService.save(swap); 11 } INJECTION
  53. Injection 1 private ProductService productService; 2 3 public void setProductService(ProductService

    injectedService) { 4 this.productService = injectedService; 5 } 6 7 public void validateSwap(String id) { 8 Swap swap = (Swap) productService.getProduct(id); 9 swap.updateState("VALIDATE"); 10 productService.save(swap); 11 } UTILISATION
  54. Injection 1 @InjectMocks 2 private BankAccount bankAccount; 3 4 @Mock

    5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 12 Mockito.doNothing().when(productService).save(swap); 13 14 bankAccount.validateSwap("fakeId"); 15 16 assertEquals("VALIDATE", swap.getState()); 17 }
  55. Injection 1 @InjectMocks 2 private BankAccount bankAccount; 3 4 @Mock

    5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 12 Mockito.doNothing().when(productService).save(swap); 13 14 bankAccount.validateSwap("fakeId"); 15 16 assertEquals("VALIDATE", swap.getState()); 17 } MOCK
  56. Injection 1 @InjectMocks 2 private BankAccount bankAccount; 3 4 @Mock

    5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 12 Mockito.doNothing().when(productService).save(swap); 13 14 bankAccount.validateSwap("fakeId"); 15 16 assertEquals("VALIDATE", swap.getState()); 17 } INJECTION AUTOMATIQUE DES MOCKS
  57. Injection 1 @InjectMocks 2 private BankAccount bankAccount; 3 4 @Mock

    5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 12 Mockito.doNothing().when(productService).save(swap); 13 14 bankAccount.validateSwap("fakeId"); 15 16 assertEquals("VALIDATE", swap.getState()); 17 }
  58. Sans injecteur ? 1 public void validateSwap(String id) { 2

    Swap swap = (Swap) ProductService.getInstance().getProduct(id); 3 swap.updateState("VALIDATE"); 4 ProductService.getInstance().save(swap); 5 }
  59. Sans injecteur ? 1 public void validateSwap(String id) { 2

    Swap swap = (Swap) ProductService.getInstance().getProduct(id); 3 swap.updateState("VALIDATE"); 4 ProductService.getInstance().save(swap); 5 } EXTRACT METHOD !
  60. Sans injecteur ? 1 public ProductService getProductService() { 2 return

    ProductService.getInstance(); 3 } 4 5 public void validateSwap(String id) { 6 Swap swap = (Swap) getProductService().getProduct(id); 7 swap.updateState("VALIDATE"); 8 getProductService().save(swap); 9 }
  61. Sans injecteur ? 1 @Spy 2 private BankAccount bankAccount =

    new BankAccount("", 23, "", 3); 3 4 @Mock 5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(productService).when(bankAccount).getProductService(); 12 13 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 14 Mockito.doNothing().when(productService).save(swap); 15 16 bankAccount.validateSwap("fakeId"); 17 18 assertEquals("VALIDATE", swap.getState()); 19 }
  62. Sans injecteur ? 1 @Spy 2 private BankAccount bankAccount =

    new BankAccount("", 23, "", 3); 3 4 @Mock 5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(productService).when(bankAccount).getProductService(); 12 13 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 14 Mockito.doNothing().when(productService).save(swap); 15 16 bankAccount.validateSwap("fakeId"); 17 18 assertEquals("VALIDATE", swap.getState()); 19 } REDÉFINITION DES MÉTHODES POSSIBLE
  63. Sans injecteur ? 1 @Spy 2 private BankAccount bankAccount =

    new BankAccount("", 23, "", 3); 3 4 @Mock 5 private ProductService productService; 6 7 @Test 8 public void can_validate_swap() { 9 Swap swap = new Swap(); 10 11 Mockito.doReturn(productService).when(bankAccount).getProductService(); 12 13 Mockito.doReturn(swap).when(productService).getProduct("fakeId"); 14 Mockito.doNothing().when(productService).save(swap); 15 16 bankAccount.validateSwap("fakeId"); 17 18 assertEquals("VALIDATE", swap.getState()); 19 }
  64. Singleton ? 1 public class ProductService { 2 3 private

    static ProductService instance = new ProductService(); 4 5 private ProductService() { 6 7 } 8 9 public static ProductService getInstance() { 10 return instance; 11 } 12 }
  65. Singleton ? 1 public class ProductService { 2 3 private

    static ProductService instance = new ProductService(); 4 5 private ProductService() { 6 7 } 8 9 public static ProductService getInstance() { 10 return instance; 11 } 12 } INSTANCIATION IMPOSSIBLE
  66. Singleton ? 1 public class ProductService { 2 3 private

    static ProductService instance = new ProductService(); 4 5 private ProductService() { 6 7 } 8 9 public static ProductService getInstance() { 10 return instance; 11 } 12 } CRÉATION DE L’INSTANCE AU CHARGEMENT DE LA CLASS
  67. Singleton ? 1 public class ProductService { 2 3 private

    static ProductService instance = null; 4 5 public ProductService() { 6 7 } 8 9 public static ProductService getInstance() { 10 if(instance == null) { 11 instance = new ProductService(); 12 } 13 return instance; 14 } 15 } CHARGEMENT TARDIF CONSTRUCTEUR PUBLIC
  68. Singleton ? 1 public class ProductService implements IProductService { 2

    3 private static IProductService instance = new ProductService(); 4 5 private ProductService() { 6 7 } 8 9 public static IProductService getInstance() { 10 return instance; 11 } 12 } NIVEAU SUPPLÉMENTAIRE VIA UNE INTERFACE