# 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>
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.*;
# 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));
}
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());
}
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.");
}
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");
}
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.");
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 etisEqualToComparingOnlyGivenFields
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");
}
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();
}
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);
}
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);
}
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);
}
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);
}
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);
}
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();
}
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");
}
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);
}
2
3
4
5
6
7
8
9
10
11