• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Tests unitaires mock_kesako_20130516
 
Like this presentation? Why not share!

Tests unitaires mock_kesako_20130516

on

  • 1,277 views

Vous subissez les régressions à chaque livraison ? Vous ne voyez pas l’intérêt des tests unitaires car ils ne servent qu’à tester des additions ? Les tests ne sont pas applicables à votre ...

Vous subissez les régressions à chaque livraison ? Vous ne voyez pas l’intérêt 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 David dans la quête du Test Driven Development. Vous rencontrerez pléthore d’ennemies contre lesquels vous aurez à combattre : bugs, complexité, code statique, couplage fort. Ils essaieront de vous barrer la route, mais heureusement, vous pourrez compter sur vos alliés jUnit, Mockito, refactoring et injection/dépendance.
David finira la soirée par une démonstration pratique sur un exercice de refactoring.

Statistics

Views

Total Views
1,277
Views on SlideShare
1,117
Embed Views
160

Actions

Likes
0
Downloads
27
Comments
0

2 Embeds 160

http://www.soat.fr 159
https://twitter.com 1

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Tests unitaires mock_kesako_20130516 Tests unitaires mock_kesako_20130516 Presentation Transcript

    • 16 mai 13Test unitaire ?Mock ? TDD ?Kezako ?1
    • Bienvenue !
    • ❤=+
    • réseaux sociauxProchaines conférences : http://soat.frtwitter : @soatgroup / @soatexpertsjavaslideshares : http://fr.slideshare.net/soatexpertAller plus loin : http://blog.soat.fr
    • Test unitaire ? Mock ? TDD ? Kezako ?en finir avec les régressions par David Wursteisen16 MAI 2013
    • Nouveau client !
    • Nouveau projet !
    • HTTP://UPLOAD.WIKIMEDIA.ORG/WIKIPEDIA/COMMONS/THUMB/6/69/HUMAN_EVOLUTION.SVG/1000PX-HUMAN_EVOLUTION.SVG.PNGLEGACY CODE
    • 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;}
    • La documentation explique le code1 /**2 *3 * @param xml : document xml représentant le swap4 * @return objet Swap5 */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 }1000LIGNES !
    • La documentation a raison1 /**2 * Always returns true.3 */4 public boolean isAvailable() {5 return false;6 }
    • La documentation a raison1 /**2 * Always returns true.3 */4 public boolean isAvailable() {5 return false;6 }WTF ?!?
    • La documentation n’est plusfiableune aideutile
    • 4 obj = networkService.getObject("product", id);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 }Faire du code qui marche
    • 4 obj = networkService.getObject("product", id);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 }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 }2 Object obj = null;5 } catch (IOException e) {6 System.err.println("Error with obj : " + obj.toString());7 }Faire du code qui marcheNullPointerException !
    • 4 account = (BankAccount) service.getProduct(this.name);5 account.deposit();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 ===");89 }Code simpleHTTP://EN.WIKIPEDIA.ORG/WIKI/FILE:JEU_DE_MIKADO.JPG
    • 4 account = (BankAccount) service.getProduct(this.name);5 account.deposit();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 ===");89 }Code simpleHTTP://EN.WIKIPEDIA.ORG/WIKI/FILE:JEU_DE_MIKADO.JPG
    • 4 account = (BankAccount) service.getProduct(this.name);5 account.deposit();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 ===");89 }Code simpleHTTP://EN.WIKIPEDIA.ORG/WIKI/FILE:JEU_DE_MIKADO.JPG
    • C’est compliqué de fairesimpleun testune évolution
    • Bonne nouvelle !
    • Je vous prend dans mon équipe !
    • HTTP://WWW.SXC.HU/PROFILE/HOTBLACKCOMMENT VAT’ON POUVOIRS’EN SORTIR ?
    • HTTP://UPLOAD.WIKIMEDIA.ORG/WIKIPEDIA/COMMONS/THUMB/6/69/HUMAN_EVOLUTION.SVG/1000PX-HUMAN_EVOLUTION.SVG.PNGTEST DRIVEN DEVELOPMENT
    • PRINCIPE DE BASE
    • Ecrituredu test
    • Lancementdu testEcrituredu test
    • Lancementdu testCorrection del’implémentationEcrituredu testKO
    • Lancementdu testCorrection del’implémentationEcrituredu testFinKOOK
    • Lancementdu testCorrection del’implémentationEcrituredu testFinKOOKREFACTOR
    • Toujours faire le test cassant avantle test passant(pour tester le test)
    • TEST UNITAIREtester la plus petite unité de code possible
    • TEST UNITAIREtester la plus petite unité de code possibleSIMPLE
    • TEST UNITAIREtester la plus petite unité de code possibleSIMPLEFEEDBACK
    • TEST UNITAIREtester la plus petite unité de code possibleSIMPLEFEEDBACKCOÛT
    • $400MILLIONS
    • EXEMPLE
    • 1 @Test2 public void can_deposit() {3 bankAccount.setBalance(50);45 boolean result = bankAccount.deposit(1000);67 assertTrue(result);8 assertEquals(1050, bankAccount.getBalance());9 }3 bankAccount.setBalance(50);Test d’abord !
    • 1 @Test2 public void can_deposit() {3 bankAccount.setBalance(50);45 boolean result = bankAccount.deposit(1000);67 assertTrue(result);8 assertEquals(1050, bankAccount.getBalance());9 }3 bankAccount.setBalance(50);Test d’abord !
    • 5 boolean result = bankAccount.deposit(1000);1 @Test2 public void can_deposit() {3 bankAccount.setBalance(50);45 boolean result = bankAccount.deposit(1000);67 assertTrue(result);8 assertEquals(1050, bankAccount.getBalance());9 }Test d’abord !
    • 1 @Test2 public void can_deposit() {3 bankAccount.setBalance(50);45 boolean result = bankAccount.deposit(1000);67 assertTrue(result);8 assertEquals(1050, bankAccount.getBalance());9 }Test d’abord !7 assertTrue(result);8 assertEquals(1050, bankAccount.getBalance());
    • Test d’abord !1 boolean deposit(int amount) {2 return false;3 }
    • Test d’abord !1 boolean deposit(int amount) {2 return false;3 }
    • Test d’abord !1 boolean deposit(int amount) {2 bal = bal + amount;3 return true;4 }
    • Test d’abord !1 boolean deposit(int amount) {2 bal = bal + amount;3 return true;4 }
    • Test d’abord !1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }
    • Test d’abord !1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }3 bankAccount.setBalance(100);
    • Test d’abord !1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }5 boolean result = bankAccount.deposit(-20);
    • Test d’abord !1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }7 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());
    • Test d’abord !1 boolean deposit(int amount) {2 bal = bal + amount;3 return true;4 }
    • Test d’abord !1 boolean deposit(int amount) {2 bal = bal + amount;3 return true;4 }
    • Test d’abord !1 boolean deposit(int amount) {2 if(amount < 0) {3 return false;4 }5 bal = bal + amount;6 return true;7 }
    • Test d’abord !1 boolean deposit(int amount) {2 if(amount < 0) {3 return false;4 }5 bal = bal + amount;6 return true;7 }
    • Acteur / Action / Assertion
    • les «3 A» : Acteur/Action/Assertion1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }
    • 1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }les «3 A» : Acteur/Action/Assertion3 bankAccount.setBalance(100);Acteur
    • 1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }les «3 A» : Acteur/Action/Assertion5 boolean result = bankAccount.deposit(-20);Action
    • 1 @Test2 public void cant_deposit_with_negative_amount() {3 bankAccount.setBalance(100);45 boolean result = bankAccount.deposit(-20);67 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());9 }les «3 A» : Acteur/Action/Assertion7 assertFalse(result);8 assertEquals(100, bankAccount.getBalance());Assertions
    • KISS
    • KISSKEEP IT SIMPLE (AND STUPID)
    • KISSKEEP IT SIMPLE (AND STUPID)
    • 7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99");89 // Vérifier.10 Assert.assertNotNull("Extra non trouvé.", targetDTO);11 Assert.assertEquals("Les accountId doivent être identiques.",12 "ABC99", targetDTO.getAccountId());1 @Test2 public void testGetCustomerOK() {34 LOGGER.info("======= testGetCustomerOK starting...");56 try {7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99");89 // Vérifier.10 Assert.assertNotNull("Extra non trouvé.", targetDTO);11 Assert.assertEquals("Les accountId doivent être identiques.",12 "ABC99", targetDTO.getAccountId());1314 } 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 }3031 LOGGER.info("======= testGetCustomerOK done.");32 }
    • 7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99");89 // Vérifier.10 Assert.assertNotNull("Extra non trouvé.", targetDTO);11 Assert.assertEquals("Les accountId doivent être identiques.",12 "ABC99", targetDTO.getAccountId());1 @Test2 public void testGetCustomerOK() {34 LOGGER.info("======= testGetCustomerOK starting...");56 try {7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99");89 // Vérifier.10 Assert.assertNotNull("Extra non trouvé.", targetDTO);11 Assert.assertEquals("Les accountId doivent être identiques.",12 "ABC99", targetDTO.getAccountId());1314 } 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 }3031 LOGGER.info("======= testGetCustomerOK done.");32 }
    • 1 @Test2 public void testGetCustomerOK() {34 LOGGER.info("======= testGetCustomerOK starting...");56 try {7 CustomerDTO targetDTO = this.serviceImpl.getCustomer("ABC99");89 // Vérifier.10 Assert.assertNotNull("Extra non trouvé.", targetDTO);11 Assert.assertEquals("Les accountId doivent être identiques.",12 "ABC99", targetDTO.getAccountId());1314 } 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 }3031 LOGGER.info("======= testGetCustomerOK done.");32 }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 }3031 LOGGER.info("======= testGetCustomerOK done.");BRUIT
    • KISS !1 @Test2 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 }
    • HTTP://UPLOAD.WIKIMEDIA.ORG/WIKIPEDIA/COMMONS/THUMB/6/69/HUMAN_EVOLUTION.SVG/1000PX-HUMAN_EVOLUTION.SVG.PNGEN PRATIQUE
    • VENIR ARMÉ
    • JUNIT + MOCKITO + ASSERTJ
    • JUNIT + MOCKITO + ASSERTJ
    • Mock ?simulacrese fait passer pour ce qu’iln’est pascomportementparamétrable
    • MOCKITO
    • doReturn(new BankAccount()).when(daoService).getObject("product", "id");
    • doReturn(new BankAccount()).when(daoService).getObject("product", "id");doReturn(new BankAccount())
    • doReturn(new BankAccount()).when(daoService).getObject("product", "id");when(daoService)
    • doReturn(new BankAccount()).when(daoService).getObject("product", "id");getObject("product", "id");
    • doThrow(IOException.class).when(daoService).getObject("product", "id");
    • doThrow(IOException.class).when(daoService).getObject("product", "id");doThrow(IOException.class)
    • doThrow(IOException.class).when(daoService).getObject("product", "id");when(daoService)
    • doThrow(IOException.class).when(daoService).getObject("product", "id");getObject("product", "id");
    • ASSERTJ
    • assertThat(age).isGreaterThan(5);
    • assertThat(myList).containsExactly("MONAD42", "META18")
    • assertThat(negotation).isBooked();
    • NOUVEAU CODE = NOUVEAU TEST
    • NOUVEAU BUG = NOUVEAU TEST
    • nouveau bug = nouveau test1 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 }
    • 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 }nouveau bug = nouveau test6 System.err.println("Error with obj : " + obj.toString());LE BUG EST ICI !
    • nouveau bug = nouveau test1 @Test2 public void can_return_null_when_ioexception() throws IOException {3 Mockito.doThrow(IOException.class)4 .when(networkService).getObject("product", "azerty");56 Object product = service.getProduct("azerty");78 assertThat(product).isNull();9 }
    • 3 Mockito.doThrow(IOException.class)4 .when(networkService).getObject("product", "azerty");1 @Test2 public void can_return_null_when_ioexception() throws IOException {3 Mockito.doThrow(IOException.class)4 .when(networkService).getObject("product", "azerty");56 Object product = service.getProduct("azerty");78 assertThat(product).isNull();9 }nouveau bug = nouveau test
    • 1 @Test2 public void can_return_null_when_ioexception() throws IOException {3 Mockito.doThrow(IOException.class)4 .when(networkService).getObject("product", "azerty");56 Object product = service.getProduct("azerty");78 assertThat(product).isNull();9 }6 Object product = service.getProduct("azerty");nouveau bug = nouveau test
    • 8 assertThat(product).isNull();1 @Test2 public void can_return_null_when_ioexception() throws IOException {3 Mockito.doThrow(IOException.class)4 .when(networkService).getObject("product", "azerty");56 Object product = service.getProduct("azerty");78 assertThat(product).isNull();9 }nouveau bug = nouveau test
    • nouveau bug = nouveau test1 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 }
    • nouveau bug = nouveau test1 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 }
    • 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 }nouveau bug = nouveau test6 System.err.println("Error with obj : " + obj.toString());LE BUG EST TOUJOURS ICI !
    • 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 }nouveau bug = nouveau test6 System.err.println("Error with obj with id: " + id);
    • nouveau bug = nouveau test1 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 }6 System.err.println("Error with obj with id: " + id);
    • Laissez votre câble tranquille
    • TESTER DOIT ÊTRE SIMPLE
    • code complexe1 /**2 *3 * @param xml : document xml représentant le swap4 * @return objet Swap5 */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 }
    • 1 @Test2 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/licensen" +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 "tt<messageId messageIdScheme="http://www.example.com/messageId">CEN/2004/01/05/15-38</messageId>n"+12 "tt<sentBy>ABC</sentBy>n" +13 "tt<sendTo>DEF</sendTo>n" +14 "tt<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 "tt<partyTradeIdentifier>n" +19 "ttt<partyReference href="abc"/>n" +20 "ttt<tradeId tradeIdScheme="http://www.abc.com/tradeId/">trade1abcxxx</tradeId>n" +21 "tt</partyTradeIdentifier>n" +22 "tt<partyTradeIdentifier>n" +23 "ttt<partyReference href="def"/>n" +24 "ttt<tradeId tradeIdScheme="http://www.def.com/tradeId/">123cds</tradeId>n" +25 "tt</partyTradeIdentifier>n" +26 "t</tradeIdentifyingItems>n" +27 "t<adjustedPaymentDate>2005-01-31</adjustedPaymentDate>n" +28 "t<netPayment>n" +29 "tt<identifier netPaymentIdScheme="http://www.centralservice.com/netPaymentId">netPaymentABCDEF001</identifier>n" +30 "tt<payerPartyReference href="abc"/>n" +31 "tt<receiverPartyReference href="def"/>n" +32 "tt<paymentAmount>n" +
    • code complexe1 /**2 *3 * @param xml : document xml représentant le swap4 * @return objet Swap5 */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 }
    • 1 /**2 *3 * @param xml : document xml représentant le swap4 * @return objet Swap5 */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 }code complexe531 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 }EXTRACT METHOD !
    • code complexe1 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 }
    • code complexe1 @Test2 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 }
    • LE CODE DOIT ÊTRE MODULAIRE
    • Singleton1 public class ProductService {23 private static ProductService instance = new ProductService();45 private DAOService daoService;67 private ProductService() {8 System.out.println("ProductService constructor");9 daoService = DAOService.getInstance();10 }1112 public static ProductService getInstance() {13 return instance;14 }15161718 public Object getProduct(String id) {19 return daoService.getObject("product", id);20 }21 }
    • 1 public class ProductService {23 private static ProductService instance = new ProductService();45 private DAOService daoService;67 private ProductService() {8 System.out.println("ProductService constructor");9 daoService = DAOService.getInstance();10 }1112 public static ProductService getInstance() {13 return instance;14 }15161718 public Object getProduct(String id) {19 return daoService.getObject("product", id);20 }21 }Singleton3 private static ProductService instance = new ProductService();
    • 1 public class ProductService {23 private static ProductService instance = new ProductService();45 private DAOService daoService;67 private ProductService() {8 System.out.println("ProductService constructor");9 daoService = DAOService.getInstance();10 }1112 public static ProductService getInstance() {13 return instance;14 }15161718 public Object getProduct(String id) {19 return daoService.getObject("product", id);20 }21 }Singleton7 private ProductService() {9 daoService = DAOService.getInstance();10 }
    • Singleton1 public class ProductService {23 private static ProductService instance = new ProductService();45 private DAOService daoService;67 private ProductService() {8 System.out.println("ProductService constructor");9 daoService = DAOService.getInstance();10 }1112 public static ProductService getInstance() {13 return instance;14 }15161718 public Object getProduct(String id) {19 return daoService.getObject("product", id);20 }21 }
    • SingletonCode
    • SingletonCode ProductService
    • SingletonCode ProductService DAOService
    • SingletonCode ProductService DAOService
    • SingletonCode ProductService DAOService
    • SingletonCode ProductService DAOServiceMockProductService
    • Singleton1 public void validateSwap(String id) {2 Swap swap = (Swap) ProductService.getInstance().getProduct(id);3 swap.updateState("VALIDATE");4 ProductService.getInstance().save(swap);5 }
    • 1 public void validateSwap(String id) {2 Swap swap = (Swap) ProductService.getInstance().getProduct(id);3 swap.updateState("VALIDATE");4 ProductService.getInstance().save(swap);5 }SingletonProductService.getInstance()4 ProductService.getInstance()COUPLAGE FORT
    • Injection1 private ProductService productService;23 public void setProductService(ProductService injectedService) {4 this.productService = injectedService;5 }67 public void validateSwap(String id) {8 Swap swap = (Swap) productService.getProduct(id);9 swap.updateState("VALIDATE");10 productService.save(swap);11 }
    • 1 private ProductService productService;23 public void setProductService(ProductService injectedService) {4 this.productService = injectedService;5 }67 public void validateSwap(String id) {8 Swap swap = (Swap) productService.getProduct(id);9 swap.updateState("VALIDATE");10 productService.save(swap);11 }Injection3 public void setProductService(ProductService injectedService) {4 this.productService = injectedService;5 }INJECTION
    • 1 private ProductService productService;23 public void setProductService(ProductService injectedService) {4 this.productService = injectedService;5 }67 public void validateSwap(String id) {8 Swap swap = (Swap) productService.getProduct(id);9 swap.updateState("VALIDATE");10 productService.save(swap);11 }Injection8 Swap swap = (Swap) productService.getProduct(id);UTILISATION
    • Injection1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }
    • 1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }Injection4 @Mock5 private ProductService productService;MOCK
    • 1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }Injection1 @InjectMocks2 private BankAccount bankAccount;INJECTION AUTOMATIQUEDES MOCKS
    • 1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }Injection11 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);
    • 1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }14 bankAccount.validateSwap("fakeId");Injection
    • Injection1 @InjectMocks2 private BankAccount bankAccount;34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(swap).when(productService).getProduct("fakeId");12 Mockito.doNothing().when(productService).save(swap);1314 bankAccount.validateSwap("fakeId");1516 assertEquals("VALIDATE", swap.getState());17 }16 assertEquals("VALIDATE", swap.getState());
    • YODA (C) DISNEY
    • UTILISE L’INJECTION,LUKEYODA (C) DISNEY
    • 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 }
    • 1 public void validateSwap(String id) {2 Swap swap = (Swap) ProductService.getInstance().getProduct(id);3 swap.updateState("VALIDATE");4 ProductService.getInstance().save(swap);5 }Sans injecteur ?ProductService.getInstance()4 ProductService.getInstance()EXTRACT METHOD !
    • 1 public ProductService getProductService() {2 return ProductService.getInstance();3 }45 public void validateSwap(String id) {6 Swap swap = (Swap) getProductService().getProduct(id);7 swap.updateState("VALIDATE");8 getProductService().save(swap);9 }Sans injecteur ?1 public ProductService getProductService() {2 return ProductService.getInstance();3 }getProductService()8 getProductService()
    • Sans injecteur ?1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }
    • 1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }Sans injecteur ?1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);REDÉFINITION DES MÉTHODESPOSSIBLE
    • 1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }Sans injecteur ?11 Mockito.doReturn(productService).when(bankAccount).getProductService();
    • 1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }Sans injecteur ?13 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);
    • Sans injecteur ?1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }16 bankAccount.validateSwap("fakeId");
    • Sans injecteur ?1 @Spy2 private BankAccount bankAccount = new BankAccount("", 23, "", 3);34 @Mock5 private ProductService productService;67 @Test8 public void can_validate_swap() {9 Swap swap = new Swap();1011 Mockito.doReturn(productService).when(bankAccount).getProductService();1213 Mockito.doReturn(swap).when(productService).getProduct("fakeId");14 Mockito.doNothing().when(productService).save(swap);1516 bankAccount.validateSwap("fakeId");1718 assertEquals("VALIDATE", swap.getState());19 }18 assertEquals("VALIDATE", swap.getState());
    • Avertissement
    • LANCER UN TEST DOIT ÊTRE FACILE
    • Tests dans son IDEECLIPSENETBEANSINTELLIJ
    • LE CODE DOIT TOUJOURSÊTRE DÉPLOYABLE
    • Intégration continuemvn install
    • Intégration continue[INFO] ---------------------------------------------[INFO] BUILD SUCCESS[INFO] ---------------------------------------------
    • Intégration continuemvn install
    • Intégration continue[INFO] ---------------------------------------------[INFO] BUILD FAILURE[INFO] ---------------------------------------------
    • Intégration continue[INFO] ---------------------------------------------[INFO] BUILD FAILURE[INFO] ---------------------------------------------WORKS ONMYMACHINE
    • Intégration continueDéclenchementd’un build
    • Intégration continueDéclenchementd’un buildCompilation
    • Intégration continueDéclenchementd’un buildCompilation Test
    • Intégration continueDéclenchementd’un buildCompilation TestDéploiementAnalyse de code
    • AVOIR LA BONNE COUVERTURE
    • 80%DE COUVERTURE DE CODE
    • 80%DE COUVERTURE DE GETTER/SETTER
    • TRAVAILLER EN BINÔME
    • binomage (pair hero)philosophie différente pour code identiqueHTTP://WWW.SXC.HU/PHOTO/959091
    • binomage (pair hero)philosophie différente pour code identiqueLES COMMANDES
    • binomage (pair hero)philosophie différente pour code identiqueLE PLAN DE VOL
    • Ping Pong
    • J’écris le test
    • Il écrit l’implémentation
    • J’écris le test
    • Il écrit l’implémentation
    • bref
    • j’ai binômé
    • 6 mois plus tard...
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • Bugs022,54567,590Janvier Février Mars Avril Mai JuinBugs Satisfaction
    • BRAVO À NOTRE ÉQUIPE DE CHOC !
    • (POUR RÉSUMER)
    • Installez Jenkins
    • Automatisez votre build
    • Écrivez votre 1er test
    • (même si il est «trop» simple)
    • HTTP://WWW.SXC.HU/PHOTO/664214IL FAUT APPRENDREÀ NAGER...
    • ...AVANT DE GAGNERDES COMPÉTITIONSHTTP://WWW.SXC.HU/PHOTO/1008962
    • ( DEMO )
    • HTTP://UPLOAD.WIKIMEDIA.ORG/WIKIPEDIA/COMMONS/THUMB/6/69/HUMAN_EVOLUTION.SVG/1000PX-HUMAN_EVOLUTION.SVG.PNGQUESTIONS ?
    • Merci !
    • It’s Pizza Time !Drink
    • @DWURSTEISEN
    • référencejenkins : http://jenkins-ci.org/assertj : https://github.com/joel-costigliola/assertjmockito : https://code.google.com/p/mockito/démo : http://parleys.com/play/5148922a0364bc17fc56c85a/about