La suite de l'article "Lire et parser les ressources en Java facilement". Nous allons voir comment utiliser les extensions de @InjectResources pour les frameworks de tests JUnit 5 et 4.
Chez Adelean, on travaille beaucoup avec Elasticsearch, et par conséquent on a l’habitude de manipuler des gros objets JSON. Et ce n’est pas surprenant que 90% de nos tests consistent à comparer les documents JSON.
Dans l’article “Lire et parser les ressources en Java facilement”, nous avons vu pourquoi lire les fichiers ressources en Java est plus compliqué que ça ne devrait être. Nous avons vu également comment simplifier cette tâche avec la librairie @InjectResources. Dans cet article, nous verrons comment injecter le contenu de fichiers ressources dans nos tests JUnit (4 et 5).
@InjectResources
fournit les extensions pour les frameworks de tests
JUnit5
et JUnit4.
Commonçons par les ajouter en tant que dépendances:
Avec Gradle:
repositories {
jcenter()
}
dependencies {
testCompile group: 'com.adelean', name: 'inject-resources-core', version: '0.1.0'
// For JUnit5
testCompile group: 'com.adelean', name: 'inject-resources-junit-jupiter', version: '0.1.0'
// or for JUnit4
testCompile group: 'com.adelean', name: 'inject-resources-junit-vintage', version: '0.1.0'
}
Ou avec Maven:
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>bintray</name>
<url>https://jcenter.bintray.com</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.adelean</groupId>
<artifactId>inject-resources-core</artifactId>
<version>0.1.0</version>
<scope>test</scope>
</dependency>
<!-- For JUnit5 -->
<dependency>
<groupId>com.adelean</groupId>
<artifactId>inject-resources-junit-jupiter</artifactId>
<version>0.1.0</version>
<scope>test</scope>
</dependency>
<!-- or for JUnit4 -->
<dependency>
<groupId>com.adelean</groupId>
<artifactId>inject-resources-junit-vintage</artifactId>
<version>0.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Supposons que nous avons un fichier ressource qui s’appelle /com/adelean/junit/jupiter/resource.txt
et qui contient ce texte:
The quick brown fox jumps over the lazy dog.
Pour utiliser son contenu dans nos tests JUnit5 il faut:
@TestWithResources // <-- ajouter l'extension
public class InjectTextResourcesTests {
@GivenTextResource("/com/adelean/junit/jupiter/resource.txt")
String resourceContent; // <-- injecter le contenu de la ressource
@Test
public void testInjectTextIntoStringInstanceField() {
assertThat(resourceContent)
.isEqualTo("The quick brown fox jumps over the lazy dog.");
}
}
Après cela, le contenu de resource.txt
apparait dans le champ resourceContent
et peut être utilisé dans les
tests.
JUnit4 de son côté utilise le mécanisme des Rule
s. La même opération avec JUnit4 peut être faite
de façon suivante:
import static com.adelean.inject.resources.junit.vintage.GivenResource.givenResource;
class MyTestClass {
@Rule
public ResourceRule<String> textResource = givenResource()
.text("/com/adelean/junit/jupiter/resource.txt")
.withCharset(StandardCharsets.UTF_8);
@Test
public void testWithTextResource() {
assertThat(textResource.get())
.isEqualTo("The quick brown fox jumps over the lazy dog.");
}
}
La méthode privilégiée pour créer les Rule
s de ressources c’est d’utiliser la méthode factory com.adelean.inject.ressources.junit.vintage.GivenResource.givenResource
. Il est recommandé d’importer cette méthode statiquement. Les exemples suivants vont omettre cet import.
Pour importer les ressources binaires, on peut utiliser l’annotation @GivenBinaryResource
pour JUnit5 ou le rule BinaryResource
pour JUnit4.
Example JUnit5:
@TestWithResources
class InjectBinaryResourcesTests {
@GivenBinaryResource("/com/adelean/junit/jupiter/fibonacci.bin")
byte[] fibonacci;
@Test
public void testInjectBinaryContentIntoByteArrayInstanceField() {
assertThat(fibonacci)
.contains(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89);
}
}
Example JUnit4:
class MyTestClass {
@Rule
public BinaryResource fibonacci = givenResource()
.binary("/com/adelean/junit/jupiter/fibonacci.bin");
@Test
public void testWithBinaryResource() {
assertThat(fibonacci.get())
.contains(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89);
}
}
De la même manière, on peut utiliser l’annotation @GivenPropertiesResource
ou le rule PropertiesResource
pour lire les properties Java.
Example JUnit5:
@TestWithResources
class InjectPropertiesResourcesTests {
@GivenPropertiesResource("/com/adelean/junit/jupiter/db.properties")
Properties dbProperties;
@Test
public void testInjectTextIntoStringInstanceField() {
assertThat(dbProperties)
.containsEntry("db.user", "hosuaby")
.containsEntry("db.password", "password")
.containsEntry("db.url", "localhost");
}
}
Example JUnit4:
class MyTestClass {
@Rule
public PropertiesResource dbProperties = givenResource()
.properties("/com/adelean/junit/jupiter/db.properties");
@Test
public void testWithJavaPropertiesResource() {
assertThat(dbProperties.get())
.containsEntry("db.user", "hosuaby")
.containsEntry("db.password", "password")
.containsEntry("db.url", "localhost");
}
}
On a vu comment la librairie gère les formats simples. Mais elle ne se limite pas à ça. On aborde la partie la plus intéressante, car @InjectResources
simplifie beaucoup le chargement et la lecture des ressources en formats JSON et YAML.
Pour lire le JSON, la librairie supporte nativement Jackson et GSON.
Regardons comment ça marche avec Jackson:
@TestWithResources
public class TestsWithJackson {
@WithJacksonMapper
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
/* JSON */
@GivenJsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
Map<String, Object> jsonAsMap;
@GivenJsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
JsonNode jsonNode;
@GivenJsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
Person spongeBob;
/* JSONL */
@GivenJsonLinesResource("/com/adelean/junit/jupiter/logs.jsonl")
Log[] logsAsArray;
@GivenJsonLinesResource("/com/adelean/junit/jupiter/logs.jsonl")
Collection<Log> logsAsCollection;
}
Pour que ce morceau de code marche, il faut que jackson-core
et jackson-databind
soient présents sur le classpath. La lecture des ressources JSON nécessite la déclaration de ObjectMapper
annoté avec @WithJacksonMapper
. Ensuite il suffit d’ajouter l’annotation @GivenJsonResource
pour parser le contenu en tant qu’une Map
, JsonNode
ou un POJO, ou l’annotation @GivenJsonLinesResource
pour lire le contenu en format JSON lines.
La lecture avec le GSON marche de la même manière. La documentation officielle en fournit un exemple: https://hosuaby.github.io/inject-resources/0.1.0/asciidoc/#_gson
Pour Junit4 nous avons les rules JsonResource
et JsonLinesResource
. A la différence des annotations de JUnit5 l’objet ObjectMapper
ou Gson
doit être passé dans le builder de Rule
.
class MyTestClass {
/* Parse JSON with Jackson */
@Rule
public JsonResource<Map<String, Object>> jsonAsMap = givenResource()
.json("/com/adelean/junit/jupiter/sponge-bob.json")
.parseWith(new ObjectMapper());
/* Parse JSON lines with Gson */
@Rule
public JsonLinesResource<Collection<Log>> logsAsCollection = givenResource()
.jsonLines("/com/adelean/junit/jupiter/logs.jsonl")
.parseWith(new Gson());
}
Le chargement de YAML ressemble à celui de JSON. @InjectResources
utilise la librairie Snakeyaml
qui doit être présente sur le classpath.
Pour JUnit5 utilisez les annotations @GivenYamlResource
et @GivenYamlDocumentsResource
(YAML documents c’est un format du fichier qui contient plusieurs documents YAML dans le même fichier séparés par trois tirets ---
).
@TestWithResources
class TestsWithYaml {
@WithSnakeYaml
Yaml yaml = new Yaml();
/* YAML resource with a single document */
@GivenYamlResource("/com/adelean/junit/jupiter/receipt.yml")
Map<String, Object> receipt;
@GivenYamlResource("/com/adelean/junit/jupiter/sponge-bob.yaml")
Person spongeBob;
/* YAML resource with multiple documents separated by '---' */
@GivenYamlDocumentsResource("/com/adelean/junit/jupiter/stacktrace.yaml")
List<Map<String, Object>> stacktraceAsList;
}
Pour JUnit4 il faut utiliser les rules YamlResource
ou YamlDocumentsResource
.
class MyTestClass {
/* Load and parse YAML resource */
@Rule
public YamlResource<Person> spongeBob = givenResource()
.yaml("/com/adelean/junit/jupiter/sponge-bob.yaml")
.parseWith(new Yaml());
/* Load and parse YAML documents resource */
@Rule
public YamlDocumentsResource<Log[]> logsAsArray = givenResource()
.yamlDocuments("/com/adelean/junit/jupiter/logs.yml")
.parseWith(new Yaml(new Constructor(Log.class)));
}
Nous avons terminé la présentation de @InjectResources
pour JUnit. Cette librairie, même si elle ne révolutionnera pas votre quotidien, vous économisera les précieuses minutes si vous manipulez souvent les ressources.
N’hésitez pas à aller voir la documentation officielle: