Upload
nathaniel-richand
View
2.069
Download
0
Embed Size (px)
DESCRIPTION
Le support de la présentation donné en mars 2011 au ScrumDay
Citation preview
Pratiques avancées de testsNathaniel Richand
Scrum day France 2011
Merci aux sponsors du Scrum day !
Sponsors Platinum
Sponsors Gold
Parrainage :
Restons pragmatique !
Doit-on :• Tester les messages d’exceptions ?• Tester les messages de logs ?
Contexte
TDD A BEAUCOUP AIDÉ
Certains tests vieillissent mal
Tests illisibles et fragiles
Exemple
Constat :
TDD peut :• devenir un frein, diminuer le rythme• Augmenter la rigidité du code
AMÉLIORER LA LISIBILITÉ
Tip1 : le nom donne l’intention
• testChoose1()• testChoose2()
• whenSelectNullIItem_ShouldThrowAnException()• whenSelectTwoItems_ShouldReturnTheSum()
Tip2 : Ne pas masquer les informations utiles
@DataSetpublic class BrokerDAOTest extends UnitilsTestNG {
@Test public void testGetNameByClauseInId() { BrokerDAS bdao = new BrokerDAO(); JpaUnitils.injectEntityManagerInto(bdao);
List<String> brokers = bdao.getNameByClauseInId("in (1)"); assertEquals(brokers.size(), 1); assertEquals(brokers.get(0), "Kerviel"); }}
WTF?
Tip3 : Masquer tout ce qui est inutile
• Setup & Teardown• Fixture• Creation method• Creation Builder• Static import
@Testpublic void generateNomDeFichierNacXMLTest(){ //Given NacCreatorBO nacCreator = new NacCreatorBO();
//when String nomFichier = nacCreator.generateNomDeFichierNacXML(creerDateBlanchie(2010, 01, 02), "123456", creerDateBlanchie(2010, 03, 04), 10); //then assertThat(nomFichier).isEqualTo("NAC_R_123456RE_020110_10_040310000000.xml");}
NacCreatorBO nacCreator;
@Beforepublic void init(){ nacCreator = new NacCreatorBO();}
@Testpublic void generateNomDeFichierNacXMLTest(){ //when String nomFichier = nacCreator.generateNomDeFichierNacXML
(creerDateBlanchie(2010, 01, 02), "123456", creerDateBlanchie(2010, 03, 04), 10); //then assertThat(nomFichier).isEqualTo("NAC_R_123456RE_020110_10_040310000000.xml");}
@Testpublic void testParserIntraday(){ File file = new File("src/test/resources/fr/haramis/service/commons/util/Planning_Options.xls"); List<IntradaySchedule> listIntradays = null; InputStream stream = null; try { stream = new FileInputStream(file.getAbsolutePath()); } catch (FileNotFoundException e) { stream = null; }
if (stream != null){ try { listIntradays = IntradayParser.parseExcelIntraday(stream, "DIRECT-T01"); } catch (HermesException e) { e.printStackTrace(); logger.error(e);
Assert.fail(); } logger.info(" ListIntradaysSize() " +listIntradays.size()); Assert.assertEquals(8400, listIntradays.size());
for (IntradaySchedule intradaySchedule : listIntradays) { Assert.assertEquals(24, intradaySchedule.getIntraday().getIntradaySchedules().size()); } }}
@Testpublic void testParserIntraday_OK() throws Exception{ //Given InputStream stream = getStreamFromFile("Planning_Options.xls");
//When List<IntradaySchedule> listIntradays = IntradayParser.parseExcelIntraday(stream, "DIRECT-T01");
//Then assertEquals(8400, listIntradays.size()); for (IntradaySchedule intradaySchedule : listIntradays) { assertEquals(24, intradaySchedule.getIntraday() .getIntradaySchedules().size()); }}
//GivenMap<Date, BigDecimal> charges = new
HashMap<Date, BigDecimal>();
charges.put(dateDebut_13_03_2010,new BigDecimal(QUANTITY_1000));
charges.put(addDaysToDate(dateDebut_13_03_2010, 1),
new BigDecimal(QUANTITY_3000));
//GivenMap<Date, BigDecimal> charges = new
MapCharges<Date>() .with(dateDebut_13_03_2010, QUANTITY_1000) .and(addDaysToDate(dateDebut_13_03_2010, 1)
QUANTITY_3000) .build();
/** * Builder pour créer plus facilement des Map<Date, BigDecimal> ou des
Map<String, BigDecimal> */private class MapCharges<T> { private Map<T, BigDecimal> cdcMap = new HashMap<T,
BigDecimal>();
public MapCharges<T> with(T dateDebut, int quantity){ cdcMap.put(dateDebut, new BigDecimal(quantity)); return this; } public MapCharges<T> and(T dateDebut, int quantity){ return with(dateDebut, quantity); } public Map<T, BigDecimal> build(){ return cdcMap; }}
Tip4 : try/catch fail()@Testpublic void testFindByStatutAndDate() { OrdreSpotDAS dao = new OrdreSpotDAO();
try { assertEquals(dao.findByDate("01/07/2008", "OK").size(), 2); assertEquals(dao.findByDate("01/07/2008", "KO").size(), 1); } catch (HaramisException e) { e.printStackTrace(); Assert.fail(); }}
@Testpublic void testFindByStatutAndDate() throws HaramisException{ OrdreSpotDAS dao = new OrdreSpotDAO();
assertEquals(dao.findByDate("01/07/2008", "OK").size(), 2); assertEquals(dao.findByDate("01/07/2008", "KO").size(), 1);}
Tip4 (bis) : if/else fail()
if (sd.getTimeRef() == 0) {assertEquals(sd.getPrice(), "301");
} else {fail("pas normal");
}
assertThat(sd.getTimeRef()).isEqualTo(0);
assertEquals(sd.getPrice(), "301");
Guard Assert
Tip5 : Simili de toString() dans les assert
assertEquals("Compagnie differente : "+result.getId()+" / "+ result.getName() +" / "+ result.getLocation()
, result, expectedCompany);
assertThat(result).isEqualTo(expectedCompany);
Tip6 : Magic number, magic null
//GivenSite siteNord = createSite(NORD, 10);Site siteSud = createSite(SUD, 12);
//Whenint affectations = dao.selectAffectationsNord(siteNord, siteSud, null);
//ThenassertThat(affectations).isEqualTo(10);
//GivenSite siteNord = createSite(NORD, AFFECTATION_NORD);Site siteSud = createSite(SUD, AFFECTATION_SUD);
//Whenint affectations = dao.selectAffectationsNord(siteNord, siteSud, SITE_NULL);
//ThenassertThat(affectations).isEqualTo(AFFECTATION_NORD);
Tip7 : Garder la même structure@Testpublic void calculTotalTest(){
//Givendouble[] volumes = {10.24556, 21, 43};
//Whendouble total = PositionUtils.calculTotal(UNITE_TEMPS_HORAIRE, volumes);
//ThenassertThat(total).isEqualTo(74.24556);
}
@Testpublic void calculerDateTest(){
//GivenDate date = HaramisDateUtils.creerDateBlanchie(2010, 5, 19);
//WhenString[] datesFormatees = PositionUtils.calculerDates(date, 1, UNITE_TEMPS_HORAIRE, ELECTRICTY);
//ThenassertThat(datesFormatees).hasSize(2);assertThat(datesFormatees[0]).isEqualTo("19/05/2010 01:00");assertThat(datesFormatees[1]).isEqualTo("19/05/2010 02:00");
}
Tip8 : Faire de belles assertions
• Ne surtout pas faire : – assertFalse(toto == null);– assertTrue(list.size() == 0);– assertEquals(result, null);
File emptyFile = writeFile("emptyFile", "");assertThat(emptyFile).hasSize(0);
List<String> names = Arrays.asList("Bob", ”Vince", ”Nat");assertThat(names) .hasSize(3) .contains("Vince") .doesNotHaveDuplicates();
String nullString = null;assertThat(nullString).isNull();
http://docs.codehaus.org/display/FEST/Fluent+Assertions+Module
for (Company cie : companies) {Assert.assertFalse(cie.getStatus()
.equalsIgnoreCase(ConstantsUtils.SIMPLE_STATUS_ANNULE), "Cie " + cie.getId());
}
assertThat(companies).onProperty("status").containsOnly(SIMPLE_STATUS_VALIDE);
Tip9 : Utiliser Spock
def "I plus I should equal II"() { given:
def calculator = new RomanCalculator()when: def result = calculator.add("I", "I") then: result == "II"
}
http://code.google.com/p/spock/
def "The lowest number should go at the end"() { setup: def result = 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"
}
http://www.wakaleo.com/blog/303-an-introduction-to-spock
FAIRE DES TESTS PLUS ROBUSTES
Tip1 : Pas de duplication
• Cf. Tip 3 : Masquer tout ce qui est inutile– Creation fixture– Creation builder
• Test Helper• Custom assertions
Tip2 : Tester un comportement à un seul endroit
public String formatResultToCSV(List<Object[]> positionsResultats) throws ParseException;
private static String[] calculerDeuxDates(Object[] ligne)throws ParseException;
public static String[] obtenirVolumes(Object[] ligne);
Public String formatResultToCSV(List<Object[]> positionsResultats) throws ParseException { StringBuilder sb = new StringBuilder(); for (Object[] ligne : positionsResultats) { String[] twoDates = calculerDeuxDates(ligne);
String[] newLine = new String[ligne.length -1]; newLine[0] = twoDates[0]; newLine[1] = twoDates[1]; String[] volumes = PositionUtils.obtenirVolumes(ligne); System.arraycopy(volumes, 0, newLine, 2, volumes.length); for (String elementLigne : newLine) { sb.append(elementLigne+"|"); } } return sb.toString();}
Tip3 : Eviter la réflexion@Testpublic void testReglesValidationHRM_118_KO() throws Exception { PortfolioManagerBO portfolioManagerBO = new PortfolioManagerBO(); Method reglesValidationHRM118 = null; PortfolioForm portfolioForm = new PortfolioForm(); portfolioManagerBO.setPortfolioForm(portfolioForm);
reglesValidationHRM118 = ReflectHelper.getPrivateMethod(portfolioManagerBO, "reglesValidationHRM118");
reglesValidationHRM118.setAccessible(true); reglesValidationHRM118.invoke(portfolioManagerBO);
assertFalse(portfolioForm.isMessageAVisible()); assertFalse(portfolioForm.isPopupVisible());}
@Testpublic void testReglesValidationHRM_118_OK() throws Exception { //Given PortfolioManagerBO portfolioManagerBO = new PortfolioManagerBO(); PortfolioForm portfolioForm = new PortfolioForm(); portfolioManagerBO.setPortfolioForm(portfolioForm);
//When portfolioManagerBO.reglesValidationHRM118();
//Then assertFalse(portfolioForm.isMessageAVisible()); assertFalse(portfolioForm.isPopupVisible());}
Tip4 : Quoi tester?
“Bas niveau” : état
private static String formatValue(Object removeNull) { if (removeNull == null) { return ""; } else { return removeNull.toString(); }}
“Haut niveau” : comportement
public String getPointsHoraires(String dateDeb, String dateFin, String portFolios, String password) { passwordChecker.checkPassword(password);
List<Long> listIdPortfolio = convertPortfoliosIdToLong(portFolios);
List<Object[]> pointsResultats = executeCalculPointsPortefeuille(
dateDeb, dateFin, listIdPortfolio);
String pointsCSV = formatResultToCSV(pointsResultats);
return pointsCSV ;}
Bilan
• Aimez vos tests et ils vous le rendront
• Les principes “Clean code” s’appliquent également au code de test!
Ressources
• http://www.wakaleo.com/blog• http://misko.hevery.com/
MERCI
[email protected]://blog.xebia.fr