Du code propre qui fonctionne...
maintenant!
Du code propre qui fonctionne maintenant.
public class ConvertisseurAdresseTest {
@Test
public void uneAdresseComplètePeutSAfficherSurUneLigne() {
Adresse adresse = new Adresse();
adresse.setRue("31 chemin de Bénédigue");
adresse.setCodePostal("33400");
adresse.setVille("Talence");
String chaine = new ConvertisseurAdresse(adresse).surUneLigne();
assertThat(chaine).isEqualTo("31 chemin de Bénédigue 33400 Talence");
}
}
public class ConvertisseurAdresse {
public ConvertisseurAdresse(Adresse adresse) {
}
public String surUneLigne() {
return "31 chemin de Bénédigue 33400 Talence";
}
}
public class ConvertisseurAdresse {
public ConvertisseurAdresse(Adresse adresse) {
this.adresse = adresse;
}
public String surUneLigne() {
return String.format("%s %s %s", this.adresse.getRue(),
this.adresse.getCodePostal(), this.adresse.getVille());
}
private Adresse adresse;
}
@Test
public void uneAdresseVideSAfficheVideSurUneLigne() {
Adresse adresse = new Adresse();
String chaine = new ConvertisseurAdresse(adresse).surUneLigne();
assertThat(chaine).isEqualTo("");
}
public String surUneLigne() {
String rue = this.adresse.getRue() != null ? this.adresse.getRue() + " "
: "";
String codePostal = this.adresse.getCodePostal() != null ? this.adresse
.getCodePostal() +
" " : "";
String ville = this.adresse.getVille() != null ? this.adresse.getVille()
: "";
return String.format("%s%s%s", rue, codePostal, ville);
}
public String surUneLigne() {
List<String> parties = Lists.newArrayList(adresse.getRue(),
adresse.getCodePostal(), adresse.getVille());
return Joiner.on(" ").skipNulls().join(parties);
}
TDD découple les activités de recherche de solution et respect de l'excellence technique pour les exécuter au moment le plus pertinent.
Des tests orientés résultat ?
[Test]
public function impossibleDEcouterUnEvenementPlusieursFois():void {
var ecouteur:EcouteurDEvenement = nice(EcouteurDEvenement);
_bus.ajouteEcouteur(ecouteur, _evenement);
_bus.ajouteEcouteur(ecouteur, _evenement);
_bus.evenementSurvenu(_evenement);
assertThat(ecouteur, received().method("evenementSurvenu").once());
}
[Test]
public function leLancePierreAjusteLeProjectileSiLeToucheSeDeplace():void {
_touche.deplaceVers(new Point(13, 37));
_lancePierre.ajusteLeProjectile(_touche);
assertThat(_projectile.x, equalTo(13));
assertThat(_projectile.y, equalTo(37));
}
Les doublures de test sont un mal parfois nécessaire.
Problème de test = problème de code de prod.
Un innocent recruteur saisit la description d'offre :
“Vous serez en charge d'un enfant de 5 ans en périscolaire du matin de 6h45 à 7h45 ou en périscolaire du soir de 18h30 à 20h30, 3 à 4 fois par semaine en fonction du planning déterminé par les parents.”
Il obtient le message d'erreur :
“Le numéro de téléphone que vous avez saisi est surtaxé. Nous vous remercions d'indiquer un numéro au tarif local ou un numéro de téléphone mobile.”
Un indice :
“Vous serez en charge d'un enfant de 5 ans en périscolaire du matin de 6h45 à 7h45 ou en périscolaire du soir de 18h30 à 20h30, 3 à 4 fois par semaine en fonction du planning déterminé par les parents.”
Une regex trop envahissante :
public class DetecteurDeNumeroDeTelephone {
private static final Pattern PATTERN_TELEPHONE =
Pattern.compile("(\\d(.){0,3}){9}\\d");
public List<String> detecte(String texte) {
List<String> numeros = Lists.newArrayList();
Matcher m = PATTERN_TELEPHONE.matcher(texte);
...
}
}
@Test
public void lEnsembleDesNumerosSontDetectesDansUnTexte() {
String texte = "Voici mon numéro : 06 21 03 43 22, " +
"vous pouvez me joindre au travail : 05-22-34-11-45";
List<String> numeros = this.detecteur.detecte(texte);
assertThat(numeros).containsExactly("0621034322", "0522341145");
}
@Test
public void unNumeroNePeutPasAvoirDesChiffresSeparesPar3Caracteres() {
List<String> numeros = this.detecteur.detecte("04---04-04-04---04");
assertThat(numeros).isEmpty();
}
@Test
public void unCalendrierPeutDireSiUnJourEstOuvré() {
Calendrier calendrier = new Calendrier();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
assertThat(calendrier.estOuvré(calendar.getTime())).isTrue();
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
assertThat(calendrier.estOuvré(calendar.getTime())).isFalse();
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
assertThat(calendrier.estOuvré(calendar.getTime())).isFalse();
}
En cas d'erreur :
org.junit.ComparisonFailure: Expected: false, Actual: true at unCalendrierPeutDireSiUnJourEstOuvré(TestCalendrier.java:23)
@Test
public void unJourDeLaSemaineDeTravailEstOuvré() {
compareOuvréPourLeJourEtLaValeur(Calendar.MONDAY, true);
}
@Test
public void leSamediNEstPasOuvré() {
compareOuvréPourLeJourEtLaValeur(Calendar.SATURDAY, false);
}
@Test
public void leDimancheNEstPasOuvré() {
compareOuvréPourLeJourEtLaValeur(Calendar.SUNDAY, false);
}
private void compareOuvréPourLeJourEtLaValeur(int jour, boolean attendu) {
boolean ouvré = new Calendrier().estOuvré(créeJour(jour));
assertThat(ouvré).as("Jour ouvré").isEqualTo(attendu);
}
En cas d'erreur :
org.junit.ComparisonFailure: [Jour ouvré] Expected: false, Actual: true leDimancheNEstPasUnJourOuvré(CalendrierTest.java:24)
Le test doit rester au service du développeur.
public class PublieurArticleTest {
@Test
public void laDateDePublicationDUnArticleEstLaDateCourante() {
Article article = new Article();
new PublieurArticle().publie(article);
Date datePublication = article.getDatePublication();
assertThat(datePublication).isEqualTo(new Date());
}
}
En cas d'erreur :
org.junit.ComparisonFailure: expected: java.lang.String<2013-11-17T18:42:50> but was: java.lang.String<2013-11-17T18:42:50> Expected :2013-11-17T18:42:50 Actual :2013-11-17T18:42:50 at laDateDeCréationDUnArticleEstLaDateCourante(PublieurArticleTest.java:18)
public class PublieurArticle {
public void publie(Article article) {
article.setDatePublication(new Date());
}
}
@Test
public void laDateDePublicationDUnArticleEstLaDateCourante() {
...
assertThat(datePublication).isEqualTo(new Date());
}
La date courante a évoluée!
public class PublieurArticleTest {
@Test
public void laDateDePublicationDUnArticleEstLaDateCourante() {
Article article = new Article();
new PublieurArticle().publie(article);
Date datePublication = article.getDatePublication();
assertThat(datePublication).is(plusOuMoinsMaintenant());
}
}
@Test
public void laDateDePublicationDUnArticleEstLaDateCourante() {
Article article = new Article();
new PublieurArticle(créeTempsFigé(NOEL)).publie(article);
Date datePublication = article.getDatePublication();
assertThat(datePublication).isEqualTo(NOEL);
}
private Temps créeTempsFigé(final Date date) {
return new Temps() {
@Override public Date maintenant() {
return date;
}
};
}
public class PublieurArticle {
public PublieurArticle(Temps temps) {
this.temps = temps;
}
public void publie(Article article) {
article.setDatePublication(temps.maintenant());
}
private Temps temps;
}
public interface Temps {
Date maintenant();
}
@Test
public void leFournisseurPeutFournirUnElémentAléatoirement() {
List<String> chaines = Lists.newArrayList("un", "deux", "trois");
GenerateurNombre générateur = new GenerateurNombreFige(1);
FournisseurElement fournisseur = new FournisseurElement(générateur);
String élément = fournisseur.fournisUnElémentDeLaListe(chaines);
assertThat(élément).isEqualTo("deux");
}
Les tests doivent rester fiables qu'importe l'environnement.
Tests contraignants =>
Moins de tests =>
Moins de feedbacks =>
Moins de courage =>
Moins de refactoring =>
Code moins évolutif =>
Moins d'agilité
Les mêmes exigences que pour le code de production !
Un test n'est que du code standard qui a pour seule particularité de ne pas partir en production.
Peut-on tester les méthodes privées ?
Non-sens !
Peut-on mocker les méthodes statiques ?
Non-sens !
... et bien autres choses
Fin