C'est lors de la présentation de David Gageot sur les test au Paris JUG du mois de janvier que j'ai découvert Fest-Assert. J'ai rapidement été enthousiaste sur son utilisation. En plus de sa syntaxe proche d'un langage naturel, il permet d'étendre ses assertions en fonction de ses besoins. Voici 2 façons d'utiliser ce mécanisme :


Utilisation d'une Condition.

Ce cas de figure est à utiliser lorsque l'objet que je souhaite vérifier possède déjà son objet d'assertion (StringAssert, FileAsset, ... ) ou si celle ci est simple. Il faut pour cela étendre la classe Condition<T> et implémenter la méthode public boolean matches(<T> value). Imaginons pas exemple que je souhaite vérifier que ma liste contienne un nombre impair d'éléments. Je vais écrire la conditions suivantes :

private class IsListeTailleImpaireCondition extends Condition<List<?>> { 

 
     @Override 
     public boolean matches(List value) { 
          if(value == null){ 
               return false; 
          } else { 
               return value.size()%2==1; 
          } 
     } 

} 

Son utilisation s'avère très simplement :

assertThat(maListe).is(new IsListeTailleImpaireCondition()); 

ou :

assertThat(maListe).satifies(new IsListeTailleImpaireCondition()); 

La différente entre les 2, n'est qu'au niveau sémantique, la validation est équivalente. Et si on veut vérifier le contraire alors ? Rassurez vous, pas besoin d'écrire une condition inverse, Fest-Assert possède des méthodes pour ça : .isNot(...) ou .doesNotSatisfy(...)


Créer son objet d'assertion.

Ce cas de figure est adapté aux cas où mon objet ne possède pas son objet d'assertions. Là, depuis la version 1.4 qui date d'il y a peu, c'est devenu beaucoup plus simple. : il suffit d'étendre la classe GenericAssert et d'implémenter les méthodes que je souhaite, sans oublier bien sûr de retourner une instance de l'objet d'assertion afin de pouvoir chaîner les méthodes. Imaginons que je souhaite développer mes assertions sur DateTime de jodatime :

import static org.fest.assertions.Formatting.inBrackets; 

import static org.fest.util.Strings.concat; 
import org.fest.assertions.GenericAssert; 
import org.joda.time.DateTime; 

/**
 * Assertions for <code>{@link org.joda.time.DateTime}</code>.
 */ 
public class DateTimeAssert extends GenericAssert<DateTimeAssert, DateTime>  { 

/**
 * Creates a new {@link org.joda.time.DateTimeAssert}.
 * @param actual the target to verify.
 */  

 public DateTimeAssert(DateTime actual) { 
     super(DateTimeAssert.class, actual); 
 } 

 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est avant celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */ 

 public DateTimeAssert isBefore(DateTime expected){ 
     isNotNull();   
     if(actual.isBefore(expected)) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be before to :", inBrackets(expected))); 
 } 

 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est avant ou égale à celui 
  *  passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */ 
 public DateTimeAssert isBeforeOrEquals(DateTime expected){ 
     isNotNull();   
     if(actual.compareTo(expected) <= 0) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be before to :", inBrackets(expected))); 
 }  

 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est après
  * celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */  

 public DateTimeAssert isAfter(DateTime expected){ 
     isNotNull();   
     if(actual.isAfter(expected)) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be after to :", inBrackets(expected))); 
 } 

 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est après ou égale à celui 
  * passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */  
 public DateTimeAssert isAfterOrEquals(DateTime expected){ 
     isNotNull();   
     if(actual.compareTo(expected) >= 0) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be after to :", inBrackets(expected))); 
 }  

 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est compris dans l'intervale de
  * ceux passés en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} de début d'intervallle
  * @param Le {@code org.joda.time.DateTimeAssert} de fin d'intervallle
  * @return L'objet d'assertion.
  */ 
 public DateTimeAssert isBetween(DateTime begin, DateTime end){ 
     isNotNull(); 
     if(actual.compareTo(begin) >= 0 && actual.compareTo(end) <= 0) { 
         return this; 
     } 
     throw failure(concat(actual(), " should be between :", 
                  inBrackets(begin), " and ", inBrackets(end)));   
 } 

 private String actual() { 
     return inBrackets(actual); 
 } 

} 

Pour m'en servir, je dois le rattacher à un point d'entrée, comme Assertions classe de base de Fest-Assert.

public class MyAssertions { 

 public static DateTimeAssert assertThat(DateTime actual) { 
     return new DateTimeAssert(actual); 
 } 

} 

Et pour finir, je n'aurais plus qu'à faire par exemple :

MyAssertions.assertThat(maDate).isBeetwen(debutPeriode,finPeriode);