Pratiques avancées de testsNathaniel Richand<br />Scrumday France 2011<br />
Merci aux sponsors du Scrumday !<br />Sponsors Platinum<br />Sponsors Gold<br />Parrainage :<br />
Restons pragmatique !<br />Doit-on :<br />Tester les messages d’exceptions ?<br />Tester les messages de logs ?<br />Conte...
TDD a beaucoup aidé <br />
Certains tests vieillissent mal<br />
Tests illisibleset fragiles<br />Exemple<br />
Constat :<br />TDD peut :<br />devenir un frein, diminuer le rythme<br />Augmenter la rigidité du code<br />
Améliorer la lisibilité<br />
Tip1 : le nom donnel’intention<br />testChoose1()<br />testChoose2()<br />whenSelectNullIItem_ShouldThrowAnException()<br ...
Tip2 : Ne pas masquer les informationsutiles<br />@DataSet<br />publicclassBrokerDAOTestextendsUnitilsTestNG {<br />@Test<...
Tip3 : Masquer tout ce qui est inutile<br />Setup & Teardown<br />Fixture<br />Creation method<br />Creation Builder<br />...
@Test<br />publicvoidgenerateNomDeFichierNacXMLTest(){<br />//Given<br />NacCreatorBOnacCreator = newNacCreatorBO();<br />...
NacCreatorBOnacCreator;<br />@Before<br />publicvoidinit(){<br />nacCreator= newNacCreatorBO();<br />}<br />@Test<br />pub...
@Test<br />publicvoidtestParserIntraday(){<br />    File file = new File("src/test/resources/fr/haramis/service/commons/ut...
@Test<br />publicvoidtestParserIntraday_OK() throws Exception{<br />//Given<br />InputStreamstream = getStreamFromFile("Pl...
//Given<br />Map<Date, BigDecimal> charges = newHashMap<Date, BigDecimal>();<br />charges.put(dateDebut_13_03_2010,<br />n...
//Given<br />Map<Date, BigDecimal> charges = newMapCharges<Date>()<br />  .with(dateDebut_13_03_2010, QUANTITY_1000)<br />...
/**<br /> * Builder pour créer plus facilement des Map<Date, BigDecimal> ou des Map<String, BigDecimal><br /> */<br />priv...
Tip4 : try/catch fail()<br />@Test<br />publicvoidtestFindByStatutAndDate() {<br />OrdreSpotDASdao = newOrdreSpotDAO();<br...
@Test<br />publicvoidtestFindByStatutAndDate() throwsHaramisException{<br />OrdreSpotDASdao = newOrdreSpotDAO();<br />asse...
Tip4 (bis) : if/else fail()<br />if (sd.getTimeRef() == 0) {<br />assertEquals(sd.getPrice(), "301");<br />} else{<br />fa...
assertThat(sd.getTimeRef()).isEqualTo(0);<br />assertEquals(sd.getPrice(), "301");<br />GuardAssert<br />
Tip5 : Simili de toString() dans les assert<br />assertEquals("Compagniedifferente : "+result.getId()+" / "+ result.getNam...
Tip6 : Magic number, magic null<br />//Given<br />Site siteNord = createSite(NORD, 10);<br />Site siteSud = createSite(SUD...
//Given<br />Site siteNord = createSite(NORD, AFFECTATION_NORD);<br />Site siteSud = createSite(SUD, AFFECTATION_SUD);<br ...
Tip7 : Garder la même structure<br />@Test<br />publicvoidcalculTotalTest(){<br />//Given<br />	double[] volumes = {10.245...
@Test<br />publicvoidcalculerDateTest(){<br />//Given<br />	Date date = HaramisDateUtils.creerDateBlanchie(2010, 5, 19);<b...
Tip8 : Faire de belles assertions<br />Ne surtout pas faire : <br />assertFalse(toto == null);<br />assertTrue(list.size()...
File emptyFile = writeFile("emptyFile", "");<br />assertThat(emptyFile).hasSize(0);<br />List<String> names = Arrays.asLis...
for (Companycie : companies) {<br />Assert.assertFalse(cie.getStatus()<br />		.equalsIgnoreCase(ConstantsUtils.SIMPLE_STAT...
Tip9 : Utiliser Spock<br />def"I plus I should equal II"() {<br />given:def calculator = newRomanCalculator()when:    def ...
def"The lowestnumbershould go at the end"() {<br />  setup:<br />defresult = calculator.add(a, b)expect:result == sum  whe...
Faire des tests plus robustes<br />
Tip1 : Pas de duplication<br />Cf. Tip 3 : Masquer tout ce qui est inutile<br />Creation fixture<br />Creation builder<br ...
Tip2 : Tester un comportementà un seulendroit<br />publicString formatResultToCSV(List<Object[]> positionsResultats) throw...
PublicString formatResultToCSV(List<Object[]> positionsResultats) throwsParseException {<br />StringBuildersb = newStringB...
Tip3 : Eviter la réflexion<br />@Test<br />publicvoid testReglesValidationHRM_118_KO() throws Exception {<br />PortfolioMa...
@Test<br />publicvoid testReglesValidationHRM_118_OK() throws Exception {<br />    //Given<br />PortfolioManagerBOportfoli...
Tip4 : Quoi tester?<br />“Bas niveau” : état<br />privatestaticString formatValue(Object removeNull) {<br />if(removeNull ...
“Haut niveau” : comportement<br />public String getPointsHoraires(String dateDeb, String dateFin, String portFolios, Strin...
Bilan<br />Aimez vos tests et ils vous le rendront<br />Les principes “Clean code” s’appliquent également au code de test!...
Ressources<br />http://www.wakaleo.com/blog<br />http://misko.hevery.com/<br />
Merci<br />nrichand@xebia.fr<br />http://blog.xebia.fr<br />
Upcoming SlideShare
Loading in …5
×

2011 nri-pratiques tests-avancees

2,277 views

Published on

Le support de la présentation donné en mars 2011 au ScrumDay

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,277
On SlideShare
0
From Embeds
0
Number of Embeds
1,048
Actions
Shares
0
Downloads
18
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • 3 symptomes :Plus d’assert@Test en commentaireCode commenté
  • MarketDataControllerBOTest
  • Chaque test porte une intention, un butOn ne teste pas une classe, mais plutôt un ensemble de comportement
  • Creation fixture (+varargs)Creation builder
  • Sortir les magic en constantes de classes
  • Plus facile pour détecter la méthodetestéeIncite à faire uneméthode de test = un testIncite à faire de petit tests
  • Fest
  • Fest
  • Fest
  • http://meetspock.appspot.com/Montrer:Nom de méthode en defGiven/When/Then : plus d’assertData Driven
  • http://meetspock.appspot.com/Montrer:Nom de méthode en defGiven/When/ThenData Driven
  • Inclus : Pas d’assertsur les init
  • Package private
  • Parler de dp4J : https://sites.google.com/a/mysimpatico.com/home//dp4j
  • 2011 nri-pratiques tests-avancees

    1. 1. Pratiques avancées de testsNathaniel Richand<br />Scrumday France 2011<br />
    2. 2. Merci aux sponsors du Scrumday !<br />Sponsors Platinum<br />Sponsors Gold<br />Parrainage :<br />
    3. 3.
    4. 4. Restons pragmatique !<br />Doit-on :<br />Tester les messages d’exceptions ?<br />Tester les messages de logs ?<br />Contexte<br />
    5. 5. TDD a beaucoup aidé <br />
    6. 6. Certains tests vieillissent mal<br />
    7. 7. Tests illisibleset fragiles<br />Exemple<br />
    8. 8. Constat :<br />TDD peut :<br />devenir un frein, diminuer le rythme<br />Augmenter la rigidité du code<br />
    9. 9. Améliorer la lisibilité<br />
    10. 10. Tip1 : le nom donnel’intention<br />testChoose1()<br />testChoose2()<br />whenSelectNullIItem_ShouldThrowAnException()<br />whenSelectTwoItems_ShouldReturnTheSum()<br />
    11. 11. Tip2 : Ne pas masquer les informationsutiles<br />@DataSet<br />publicclassBrokerDAOTestextendsUnitilsTestNG {<br />@Test<br />publicvoidtestGetNameByClauseInId() {<br />BrokerDASbdao = newBrokerDAO();<br />JpaUnitils.injectEntityManagerInto(bdao);<br /> List<String> brokers = bdao.getNameByClauseInId("in (1)");<br />assertEquals(brokers.size(), 1);<br />assertEquals(brokers.get(0), "Kerviel");<br /> }<br />}<br />WTF?<br />
    12. 12. Tip3 : Masquer tout ce qui est inutile<br />Setup & Teardown<br />Fixture<br />Creation method<br />Creation Builder<br />Static import<br />
    13. 13. @Test<br />publicvoidgenerateNomDeFichierNacXMLTest(){<br />//Given<br />NacCreatorBOnacCreator = newNacCreatorBO();<br />//when<br /> String nomFichier= nacCreator.generateNomDeFichierNacXML (creerDateBlanchie(2010, 01, 02), "123456", creerDateBlanchie(2010, 03, 04), 10);<br />//then<br />assertThat(nomFichier).isEqualTo ("NAC_R_123456RE_020110_10_040310000000.xml");<br />}<br />
    14. 14. NacCreatorBOnacCreator;<br />@Before<br />publicvoidinit(){<br />nacCreator= newNacCreatorBO();<br />}<br />@Test<br />publicvoidgenerateNomDeFichierNacXMLTest(){<br />//when<br /> String nomFichier = nacCreator.generateNomDeFichierNacXML (creerDateBlanchie(2010, 01, 02), "123456", creerDateBlanchie(2010, 03, 04), 10);<br />//then<br />assertThat(nomFichier).isEqualTo ("NAC_R_123456RE_020110_10_040310000000.xml");<br />}<br />
    15. 15. @Test<br />publicvoidtestParserIntraday(){<br /> File file = new File("src/test/resources/fr/haramis/service/commons/util/Planning_Options.xls");<br /> List<IntradaySchedule> listIntradays = null;<br />InputStreamstream = null;<br />try{<br />stream= newFileInputStream(file.getAbsolutePath());<br /> } catch(FileNotFoundException e) {<br />stream= null;<br /> }<br />if(stream != null){<br />try{<br />listIntradays= IntradayParser.parseExcelIntraday(stream, "DIRECT-T01");<br /> } catch(HermesException e) {<br />e.printStackTrace();<br />logger.error(e);<br />Assert.fail();<br /> }<br />logger.info(" ListIntradaysSize() " +listIntradays.size());<br />Assert.assertEquals(8400, listIntradays.size());<br />for(IntradayScheduleintradaySchedule : listIntradays) {<br />Assert.assertEquals(24, intradaySchedule.getIntraday().getIntradaySchedules().size());<br /> }<br /> }<br />}<br />
    16. 16. @Test<br />publicvoidtestParserIntraday_OK() throws Exception{<br />//Given<br />InputStreamstream = getStreamFromFile("Planning_Options.xls");<br />//When<br /> List<IntradaySchedule> listIntradays = IntradayParser.parseExcelIntraday(stream, "DIRECT-T01");<br />//Then<br />assertEquals(8400, listIntradays.size());<br /> for(IntradayScheduleintradaySchedule : listIntradays) {<br />assertEquals(24, intradaySchedule.getIntraday()<br /> .getIntradaySchedules().size());<br /> }<br />}<br />
    17. 17. //Given<br />Map<Date, BigDecimal> charges = newHashMap<Date, BigDecimal>();<br />charges.put(dateDebut_13_03_2010,<br />newBigDecimal(QUANTITY_1000));<br />charges.put(addDaysToDate(dateDebut_13_03_2010, 1),<br />newBigDecimal(QUANTITY_3000));<br />
    18. 18. //Given<br />Map<Date, BigDecimal> charges = newMapCharges<Date>()<br /> .with(dateDebut_13_03_2010, QUANTITY_1000)<br /> .and(addDaysToDate(dateDebut_13_03_2010, 1) QUANTITY_3000)<br /> .build();<br />
    19. 19. /**<br /> * Builder pour créer plus facilement des Map<Date, BigDecimal> ou des Map<String, BigDecimal><br /> */<br />privateclassMapCharges<T> {<br />privateMap<T, BigDecimal> cdcMap = newHashMap<T, BigDecimal>();<br /> publicMapCharges<T> with(T dateDebut, intquantity){<br /> cdcMap.put(dateDebut, newBigDecimal(quantity));<br /> returnthis;<br /> }<br /> publicMapCharges<T> and(T dateDebut, intquantity){<br /> returnwith(dateDebut, quantity);<br /> }<br /> publicMap<T, BigDecimal> build(){<br /> returncdcMap;<br /> }<br />}<br />
    20. 20. Tip4 : try/catch fail()<br />@Test<br />publicvoidtestFindByStatutAndDate() {<br />OrdreSpotDASdao = newOrdreSpotDAO();<br />try{<br />assertEquals(dao.findByDate("01/07/2008", "OK").size(), 2);<br />assertEquals(dao.findByDate("01/07/2008", "KO").size(), 1);<br />} catch (HaramisExceptione) {<br />e.printStackTrace();<br />Assert.fail();<br /> }<br />}<br />
    21. 21. @Test<br />publicvoidtestFindByStatutAndDate() throwsHaramisException{<br />OrdreSpotDASdao = newOrdreSpotDAO();<br />assertEquals(dao.findByDate("01/07/2008", "OK").size(), 2);<br />assertEquals(dao.findByDate("01/07/2008", "KO").size(), 1);<br />}<br />
    22. 22. Tip4 (bis) : if/else fail()<br />if (sd.getTimeRef() == 0) {<br />assertEquals(sd.getPrice(), "301");<br />} else{<br />fail("pas normal");<br />}<br />
    23. 23. assertThat(sd.getTimeRef()).isEqualTo(0);<br />assertEquals(sd.getPrice(), "301");<br />GuardAssert<br />
    24. 24. Tip5 : Simili de toString() dans les assert<br />assertEquals("Compagniedifferente : "+result.getId()+" / "+ result.getName() +" / "+ result.getLocation()<br />, result, expectedCompany);<br />assertThat(result).isEqualTo(expectedCompany);<br />
    25. 25. Tip6 : Magic number, magic null<br />//Given<br />Site siteNord = createSite(NORD, 10);<br />Site siteSud = createSite(SUD, 12);<br />//When<br />intaffectations = dao.selectAffectationsNord(siteNord, siteSud, null); <br />//Then<br />assertThat(affectations).isEqualTo(10);<br />
    26. 26. //Given<br />Site siteNord = createSite(NORD, AFFECTATION_NORD);<br />Site siteSud = createSite(SUD, AFFECTATION_SUD);<br />//When<br />intaffectations = dao.selectAffectationsNord(siteNord, siteSud, SITE_NULL); <br />//Then<br />assertThat(affectations).isEqualTo(AFFECTATION_NORD);<br />
    27. 27. Tip7 : Garder la même structure<br />@Test<br />publicvoidcalculTotalTest(){<br />//Given<br /> double[] volumes = {10.24556, 21, 43};<br /> //When<br /> double total = PositionUtils.calculTotal(UNITE_TEMPS_HORAIRE, volumes);<br /> //Then<br />assertThat(total).isEqualTo(74.24556);<br />}<br />
    28. 28. @Test<br />publicvoidcalculerDateTest(){<br />//Given<br /> Date date = HaramisDateUtils.creerDateBlanchie(2010, 5, 19);<br />//When<br /> String[] datesFormatees = PositionUtils.calculerDates(date, 1, UNITE_TEMPS_HORAIRE, ELECTRICTY);<br />//Then<br />assertThat(datesFormatees).hasSize(2);<br />assertThat(datesFormatees[0]).isEqualTo("19/05/2010 01:00");<br />assertThat(datesFormatees[1]).isEqualTo("19/05/2010 02:00");<br />}<br />
    29. 29. Tip8 : Faire de belles assertions<br />Ne surtout pas faire : <br />assertFalse(toto == null);<br />assertTrue(list.size() == 0);<br />assertEquals(result, null);<br />
    30. 30. File emptyFile = writeFile("emptyFile", "");<br />assertThat(emptyFile).hasSize(0);<br />List<String> names = Arrays.asList("Bob", ”Vince", ”Nat");<br />assertThat(names)<br /> .hasSize(3)<br /> .contains("Vince")<br /> .doesNotHaveDuplicates();<br />String nullString = null;<br />assertThat(nullString).isNull();<br />http://docs.codehaus.org/display/FEST/Fluent+Assertions+Module<br />
    31. 31. for (Companycie : companies) {<br />Assert.assertFalse(cie.getStatus()<br /> .equalsIgnoreCase(ConstantsUtils.SIMPLE_STATUS_ANNULE)<br /> , "Cie " + cie.getId());<br />}<br />assertThat(companies)<br /> .onProperty("status")<br /> .containsOnly(SIMPLE_STATUS_VALIDE);<br />
    32. 32. Tip9 : Utiliser Spock<br />def"I plus I should equal II"() {<br />given:def calculator = newRomanCalculator()when:    def result = calculator.add("I", "I")           then:    result == "II"<br />}<br />http://code.google.com/p/spock/<br />
    33. 33. def"The lowestnumbershould go at the end"() {<br /> setup:<br />defresult = calculator.add(a, b)expect:result == sum  where: a   | b  | sum    "X"  | "I" | "XI"    "I"  | "X" | "XI"    "XX" | "I" | "XXI"    "XX" | "II"| "XXII"    "II" | "XX"| "XXII"  <br />}<br />http://www.wakaleo.com/blog/303-an-introduction-to-spock<br />
    34. 34. Faire des tests plus robustes<br />
    35. 35. Tip1 : Pas de duplication<br />Cf. Tip 3 : Masquer tout ce qui est inutile<br />Creation fixture<br />Creation builder<br />Test Helper<br />Custom assertions<br />
    36. 36. Tip2 : Tester un comportementà un seulendroit<br />publicString formatResultToCSV(List<Object[]> positionsResultats) throwsParseException;<br />privatestatic String[] calculerDeuxDates(Object[] ligne)<br />throwsParseException;<br />publicstatic String[] obtenirVolumes(Object[] ligne);<br />
    37. 37. PublicString formatResultToCSV(List<Object[]> positionsResultats) throwsParseException {<br />StringBuildersb = newStringBuilder();<br /> for(Object[] ligne : positionsResultats) {<br />String[] twoDates = calculerDeuxDates(ligne);<br />String[] newLine = new String[ligne.length -1];<br />newLine[0] = twoDates[0];<br />newLine[1] = twoDates[1];<br />String[] volumes = PositionUtils.obtenirVolumes(ligne);<br />System.arraycopy(volumes, 0, newLine, 2, volumes.length);<br />for(String elementLigne : newLine) {<br />sb.append(elementLigne+"|");<br />}<br />}<br />returnsb.toString();<br />}<br />
    38. 38. Tip3 : Eviter la réflexion<br />@Test<br />publicvoid testReglesValidationHRM_118_KO() throws Exception {<br />PortfolioManagerBOportfolioManagerBO = newPortfolioManagerBO();<br />Method reglesValidationHRM118 = null;<br />PortfolioFormportfolioForm = newPortfolioForm();<br />portfolioManagerBO.setPortfolioForm(portfolioForm);<br />reglesValidationHRM118 = ReflectHelper.getPrivateMethod(portfolioManagerBO, "reglesValidationHRM118");<br /> reglesValidationHRM118.setAccessible(true);<br /> reglesValidationHRM118.invoke(portfolioManagerBO);<br />assertFalse(portfolioForm.isMessageAVisible());<br />assertFalse(portfolioForm.isPopupVisible());<br />}<br />
    39. 39. @Test<br />publicvoid testReglesValidationHRM_118_OK() throws Exception {<br /> //Given<br />PortfolioManagerBOportfolioManagerBO = newPortfolioManagerBO();<br />PortfolioFormportfolioForm = newPortfolioForm();<br />portfolioManagerBO.setPortfolioForm(portfolioForm);<br /> //When<br /> portfolioManagerBO.reglesValidationHRM118();<br /> //Then<br />assertFalse(portfolioForm.isMessageAVisible());<br />assertFalse(portfolioForm.isPopupVisible());<br />}<br />
    40. 40. Tip4 : Quoi tester?<br />“Bas niveau” : état<br />privatestaticString formatValue(Object removeNull) {<br />if(removeNull == null) {<br /> return"";<br /> } else {<br /> returnremoveNull.toString();<br /> }<br />}<br />
    41. 41. “Haut niveau” : comportement<br />public String getPointsHoraires(String dateDeb, String dateFin, String portFolios, String password) {<br />passwordChecker.checkPassword(password);<br /> List<Long> listIdPortfolio = convertPortfoliosIdToLong(portFolios);<br /> List<Object[]> pointsResultats = executeCalculPointsPortefeuille(<br />dateDeb, dateFin, listIdPortfolio);<br /> String pointsCSV= formatResultToCSV(pointsResultats);<br />returnpointsCSV;<br />}<br />
    42. 42. Bilan<br />Aimez vos tests et ils vous le rendront<br />Les principes “Clean code” s’appliquent également au code de test!<br />
    43. 43. Ressources<br />http://www.wakaleo.com/blog<br />http://misko.hevery.com/<br />
    44. 44. Merci<br />nrichand@xebia.fr<br />http://blog.xebia.fr<br />

    ×