# AssertJ Quick Start Tutoriel

# Pourquoi AssertJ

AssertJ est une libraire d'assertion avec une fluent API orienté vers l'écriture de tests lisibles et facilement maintenables. AssertJ est très bien intégré dans l'écosystème Java et s'utilise conjointement avec un framework de test comme JUnit ou TestNG (opens new window).

# Installation avec Maven

Pour commencer, il faut inclure AssertJ comme dépendance de test dans la configuration maven de son projet (pensez à vérifier la dernière version sur mvnrepository (opens new window)):

<dependencies>
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.12.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>
1
2
3
4
5
6
7
8

Si ce n'est pas déjà fait, il faut également ajouter un exécuteur de test comme JUnit 5 puisque AssertJ ne fournit que les assertions.

L'ensemble des fonctionnalités sont ensuite disponibles sous forme de méthodes statiques au niveau de la classe org.assertj.core.api.Assertions. Le plus simple est encore de les ajouter dans le test via un import static:

import static org.assertj.core.api.Assertions.*;
1

# Assertions de base

Les différentes assertions d'AssertJ sont disponibles via la méthode statique Assertions.assertThat. Il suffit de lui passer l'objet que l'on souhaite tester puis de se laisser guider par l'autocompletion:

@Test
@DisplayName("Simple assertions on String, Integer, Float")
public void baseAssertion() {
  assertThat(Country.USA.getName()).isEqualTo("United States of America");
  assertThat(Country.USA.getName()).matches("U.*S.*A.*");
  assertThat(Country.USA.getPopulation()).isGreaterThan(Country.FRANCE.getPopulation());
  assertThat(Country.USA.getPopulation())
      .isBetween(Country.FRANCE.getPopulation(), Country.CHINA.getPopulation());
  assertThat(Country.USA.getDensity())
      .as("Test float with a given precision")
      .isCloseTo(36.0f, Offset.offset(0.1f));
}
1
2
3
4
5
6
7
8
9
10
11

WARNING

Lorsqu'on teste l'égalité entre float, il ne faut pas oublier d'indiquer un delta pour tenir compte de l'imprécision des nombres à virgule flottante. AssertJ, met à disposition le classe Offset à cet effet.

# Chainer les assertions

L'un des avantages d'AssertJ est de pouvoir chainer les assertions sur un même objet grâce à son API fluent:

@Test
@DisplayName("Assertion can be chained")
public void chaining() {
  assertThat(Country.USA.getCapital()).contains("Washington").endsWith("D.C.");
  assertThat(Country.USA.getPopulation())
      .isGreaterThan(Country.FRANCE.getPopulation())
      .isLessThan(Country.CHINA.getPopulation());
}
1
2
3
4
5
6
7

# Ajouter une description de l'assertion

Pour rendre le test le plus lisible possible, il est possible d'ajouter une description aux assertions. En cas d'erreur, cette description sera remontée dans le rapport final:

@Test
@DisplayName("Describing assertion for meaningful failure report")
public void description() {
  assertThat(Country.USA.getCapital())
      .as("Testing %s capital name", Country.USA.getName())
      .isEqualTo("Washington, D.C.");
}
1
2
3
4
5
6

TIP

Le message est formatter via String.format et utilise donc la syntaxe de Formatter (opens new window)

# Extraire les propriétés

Très souvent, les tests portent sur des objects métiers et il n'y a donc pas d'assertions directement disponibles. Dans ces cas, il est possible d'extraire les propriétés de ces objets pour faire des assertions dessus. La propriété à extraire est définie soit par une fonction, soit par son nom sous forme de chaîne de caractère:

@Test
@DisplayName("Extracting the object properties to make assertion on them")
public void propertyExtractor() {
  // Assertion below are on Object
  assertThat(Country.USA).extracting(Country::getCapital).isEqualTo("Washington, D.C.");
  // Assertion below are on List<?>
  assertThat(Country.FRANCE)
      .extracting(Country::getName, Country::getCapital)
      .containsExactly("France", "Paris");
  assertThat(Country.USA).extracting("capital").containsOnly("Washington, D.C.");
  assertThat(Country.USA)
      .extracting("name", "capital")
      .containsOnly("United States of America", "Washington, D.C.");
  assertThat(Country.FRANCE)
      .extracting("name", "continent.name")
      .containsExactly("France", "Europe");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

TIP

Il est possible d'extraire les propriétés sur plusieurs niveaux en séparant leur nom par un .. Dans l'exemple ci-dessus, "continent.name" correspond à Country.getContinent().getName()

WARNING

Les extracteurs retournent un Object dans tous les cas, donc le test suivant ne marche pas, même si Country::getCapital retourne une String:

assertThat(Country.USA).extracting(Country::getCapital)
    .contains("Washington").endsWith("D.C.");
1
2

# Comparer champs à champs

L'assertion isEqualTo repose sur la méthode equals des objets. Dans certains cas, en particulier si cette méthode n'a pas été surchargée, il est préférable de tester l'égalité sur les valeurs des propriétés de l'objet. AssertJ propose les helpers:

  • isEqualToComparingFieldByField pour tester l'égalité sur l'ensemble des propriétés et
  • isEqualToComparingOnlyGivenFields pour tester l'égalité sur un sous-ensemble des propriétés.
@Test
@DisplayName("Object comparison can be made on a subset of their properties")
public void fieldByFieldComparison() {
  Country france =
      new Country.Builder("FR")
          .setName("France")
          .setContinentName("Europe")
          .setContinentCode("EU")
          .setCapital("Paris")
          .setPopulation(67272000)
          .setDensity(105.2f)
          .build();
  assertThat(Country.FRANCE).isEqualToComparingFieldByField(france);
  assertThat(Country.FRANCE).isEqualToComparingOnlyGivenFields(Country.GERMANY, "continent.name");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Assertions sur les Exceptions

AssertJ offre plusieurs helpers pour tester qu'une exception est attendue ou non à l'exécution. Afin que l'exception ne remonte pas au framework de test, il faut englober le code à tester dans une lambda. AssertJ permet ensuite de tester la classe de l'exception, sa cause ou encore son message:

@Test
@DisplayName("Checking for exceptions")
public void exceptionAssertion() {
  Country noCountryForOldMan = null;
  assertThatThrownBy(() -> noCountryForOldMan.getPopulation())
      .isInstanceOf(NullPointerException.class)
      .hasNoCause();
  assertThatExceptionOfType(NullPointerException.class)
      .isThrownBy(() -> noCountryForOldMan.getPopulation())
      .withNoCause();
  assertThatNullPointerException()
      .isThrownBy(() -> noCountryForOldMan.getPopulation())
      .withNoCause();
  assertThatCode(() -> Country.CHINA.getPopulation())
      .as("No exception expected")
      .doesNotThrowAnyException();
  Throwable thrown = catchThrowable(() -> noCountryForOldMan.getPopulation());
  assertThat(thrown).isInstanceOf(NullPointerException.class).hasNoCause();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Collections

AssertJ permet d'effectuer rapidement des tests sur les collections, que ce soit sur leur taille ou leur contenu:

@Test
@DisplayName("Chaining assertions on list")
public void simpleAssertion() {
  List<Country> countries = Arrays.asList(Country.AUSTRALIA, Country.USA, Country.CHINA);
  assertThat(countries)
      .isNotEmpty()
      .hasSize(3)
      .startsWith(Country.AUSTRALIA)
      .doesNotContainNull()
      .endsWith(Country.CHINA);
}
1
2
3
4
5
6
7
8
9
10

# Assertions sur le contenu

Le contenu d'une collection peut être examinée de manière partielle ou complète, en tenant compte de l'ordre des éléments ou non:

@Test
@DisplayName("Contains assertions on list")
public void containsAssertion() {
  List<Country> countries = Arrays.asList(Country.AUSTRALIA, Country.USA, Country.CHINA);
  assertThat(countries)
      .contains(Country.USA)
      .doesNotContain(Country.FRANCE)
      .containsAnyOf(Country.USA, Country.FRANCE)
      .containsOnly(Country.USA, Country.AUSTRALIA, Country.CHINA)
      .containsExactly(Country.AUSTRALIA, Country.USA, Country.CHINA);
}
1
2
3
4
5
6
7
8
9
10

TIP

containsOnly ne tient pas compte de l'ordre alors que containsExactly si.

# Filtrer les collections

Il est également possible de filter une collection avant de tester son contenu. Le filtre peut s'appliquer directement sur la valeur d'une propriété ou via une fonction de test appliquée à chaque élément de la collection:

@Test
@DisplayName("Filtering the list before asserting on it")
public void filterAssertion() {
  Country france =
      new Country.Builder("FR")
          .setName("France")
          .setCapital("Paris")
          .setDensity(105.2f)
          .setPopulation(null)
          .build();
  List<Country> countries = Arrays.asList(france, Country.USA, Country.CHINA);
  assertThat(countries).filteredOn("density", 105.2f).containsOnly(france);
  assertThat(countries)
      .filteredOn(country -> country.getDensity() > france.getDensity())
      .containsExactly(Country.CHINA);
  assertThat(countries).filteredOnNull("population").containsOnly(france);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Extraction des propriétés des objets d'une collection

Face à une liste d'objet complexe, il est parfois plus simple de tester un sous-ensemble des propriétés des objets de la liste plutôt que les objets en entier. Les méthodes extracting et flatExtracting permettent d'extraire une ou plusieurs propriétés des objets d'une collection soit via une fonction soit via le nom de la propriété.

@Test
@DisplayName("Extracting properties from list objects before asserting on them")
public void extractingAssertion() {
  List<Country> countries = Arrays.asList(Country.USA, Country.CHINA);
  assertThat(countries).extracting("name").containsExactly("United States of America", "China");
  assertThat(countries)
      .extracting(Country::getName)
      .containsExactly("United States of America", "China");
  assertThat(countries)
      .extracting("name", "density")
      .containsExactly(tuple("United States of America", 36.0f), tuple("China", 148.5f));
  assertThat(countries)
      .flatExtracting("name", "density")
      .containsExactly("United States of America", 36.0f, "China", 148.5f);
  assertThat(countries)
      .flatExtracting(Country::getName, Country::getDensity)
      .containsExactly("United States of America", 36.0f, "China", 148.5f);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

TIP

extracting permet d'extraire plusieurs propriétés et les groupe par objet sous forme de Tuple qu'il faut tester avec Assertions.tuple. A l'inverse flatExtracting ne groupe pas les propriétés qui doivent être testées indépendamment.

# Assertions sur les Map

En ce qui concerne les Map, autre collection fréquemment utilisée, AssertJ permet de tester simplement leur contenu, via leurs clefs, leurs valeurs ou leurs entrées:

@Test
@DisplayName("Map assertions")
public void mapAssertion() {
  List<Country> countries = Arrays.asList(Country.AUSTRALIA, Country.USA, Country.CHINA);
  Map<String, Country> countryByIso =
      countries.stream().collect(Collectors.toMap(Country::getIsoCode, Function.identity()));
  assertThat(countryByIso)
      .hasSize(3)
      .containsOnlyKeys("AU", "US", "CN")
      .containsValues(Country.USA, Country.CHINA, Country.AUSTRALIA)
      .contains(entry("US", Country.USA))
      .extracting("AU")
      .contains(Country.AUSTRALIA);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

AssertJ met à disposition le helper Assertions.entry pour tester les entrées d'une Map.

# File

# Assertions le fichier

Si votre code interagit avec des fichiers, là encore, AssertJ va vous faciliter la tâche:

@Test
public void fileAssertion(@TempDir Path path) throws IOException {
  Path filePath = path.resolve("some_file.txt");
  Files.createFile(filePath);
  assertThat(filePath.toFile()).exists().isFile().hasExtension("txt").canRead().canWrite();
}
1
2
3
4
5

# Assertions sur le contenu

Le contenu d'un fichier peut être testé:

  • complétement via hasContent,
  • par l'intermédiaire d'un digest via hasDigest ou
  • partiellement via les méthodes startsWith, contains, endsWith.
@Test
public void fileContentAssertion(@TempDir Path path)
    throws IOException, NoSuchAlgorithmException {
  Path filePath = path.resolve("some_file.txt");
  String fileContent = "Some content in the file";
  byte[] fileDigest = MessageDigest.getInstance("MD5").digest(fileContent.getBytes());
  Files.write(filePath, fileContent.getBytes());
  assertThat(filePath.toFile()).hasContent(fileContent).hasDigest("MD5", fileDigest);
  assertThat(contentOf(filePath.toFile()))
      .startsWith("Some")
      .contains("content")
      .endsWith("file");
}
1
2
3
4
5
6
7
8
9
10
11
12

TIP

Il est également possible de tester le contenu d'une InputStream:

@Test
public void inputStreamAssertion(@TempDir Path path)
    throws IOException, NoSuchAlgorithmException {
  Path filePath = path.resolve("some_file.txt");
  String fileContent = "Some content in the file";
  byte[] fileDigest = MessageDigest.getInstance("MD5").digest(fileContent.getBytes());
  Files.write(filePath, fileContent.getBytes());
  InputStream inputStream = new FileInputStream(filePath.toFile());
  assertThat(inputStream).hasDigest("MD5", fileDigest);
  inputStream = new FileInputStream(filePath.toFile());
  assertThat(inputStream).hasContent(fileContent);
}
1
2
3
4
5
6
7
8
9
10
11
Dernière mise à jour: 7/1/2019, 4:08:10 PM