Following the precedent article "A convenient way to read resources in Java", we will learn how to use @InjectResources to inject JUnit 5 and 4 tests with content of resources.
Here at Adelean, we work a lot with Elasticsearch, so it’s not uncommon for us to deal with huge JSON objects. Neither is it surprising that 90% of our tests involves comparison of JSON documents.
In the article “A convenient way to read resources in Java” we saw that reading resources with Java is harder than it should be. We also saw how to simplify this task using library @InjectResources. In this article we will see how to inject our JUnit 5/4 tests with content of resources.
@InjectResources
comes with extensions for popular test frameworks JUnit5 and JUnit4.
Let’s start by adding necessary dependencies to our project:
With 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'
}
Or with 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>
Let’s assume that we have a resource file /com/adelean/junit/jupiter/resource.txt
containing this text:
The quick brown fox jumps over the lazy dog.
To inject this text on our JUnit5 tests we need:
@TestWithResources // <-- add extension
public class InjectTextResourcesTests {
@GivenTextResource("/com/adelean/junit/jupiter/resource.txt")
String resourceContent; // <-- inject resource content
@Test
public void testInjectTextIntoStringInstanceField() {
assertThat(resourceContent)
.isEqualTo("The quick brown fox jumps over the lazy dog.");
}
}
@TestWithResources
@GivenTextResource
After that, the content of resource resource.txt
appears inside the field resourceContent
and can be used in tests.
JUnit4 on the other side uses Rule
s. The same operation with JUnit4 can be done as following:
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.");
}
}
It’s recommended to create resource rules using factory method com.adelean.inject.ressources.junit.vintage.GivenResource.givenResource
imported statically. Following examples will omit that import.
To import binary resources we can use @GivenBinaryResource
annotation for JUnit5 or BinaryResource
rule for JUnit4.
With 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);
}
}
With 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);
}
}
In a similar way, we can use annotation @GivenPropertiesResource
or PropertiesResource
rule to read Java properties.
With 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");
}
}
With 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");
}
}
We saw how the library handles simple formats. But it can do much more. We start the most interesting part of this article because @InjectResources
considerably simplifies loading and parsing of JSON and YAML.
To parse JSON library natively supports Jackson and GSON.
Let’s see how it works with 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;
}
In order for this snippet to work, jackson-core
and jackson-databind
must be present on the classpath.
JSON parsing requires declaration of ObjectMapper
annotated with @WithJacksonMapper
.
After, all we need is to add annotation @GivenJsonResource
on the field of type Map
, JsonNode
or any POJO to get parsed content of JSON, or annotation @GivenJsonLinesResource
for JSON lines.
Parsing with GSON works in similar manner. The official documentation gives us an example: https://hosuaby.github.io/inject-resources/0.1.0/asciidoc/#_gson
For Junit4 we have rules JsonResource
and JsonLinesResource
. The difference with JUnit5 annotations is that we need to supply ObjectMapper
or Gson
object to rules builder.
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());
}
Parsing of YAML is similar to JSONs. For this purpose @InjectResources
uses Snakeyaml
that must be present on the classpath.
For JUnit5 use annotations @GivenYamlResource
and @GivenYamlDocumentsResource
(YAML documents is a format of a file that contains multiple YAML documents separated by three hyphens ---
).
@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;
}
For JUnit4 use rules YamlResource
and 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)));
}
We finished this presentation of @InjectResources
for JUnit. This tool can save you a lot of time if you work with resources a lot.
Do not hesitate to take a look into official documentation: