© 2020 - 2022 by Alexei Klenin

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

1. Preface

@InjectResources is a Java library providing simple and convenient way to load and parse resource files. It does all opening and closing of input streams and handles exceptions.

Java 8+ is required in order to use this library.

2. Modules

3. Java DSL

@InjectResources fluid Java DSL helps to load and parse the resources conveniently.

3.1. Dependency

Add inject-resources-core dependency to your project.

Using Maven:

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-core</artifactId>
    <version>0.2.2</version>
</dependency>

Or using Gradle:

compile group: 'io.hosuaby', name: 'inject-resources-core', version: '0.2.2'

3.2. Java DSL example

var text = resource()
        .withPath("/com/adelean/junit/jupiter", "resource.txt")
        .text();

3.2.1. Entry point

Method InjectResources#resource is an entry point of Java DSL. It’s recommended to import this method statically:

import static com.adelean.inject.resources.core.InjectResources.resource;

3.2.2. Choosing the classloader

To choose a classloader used to load a single resource it’s possible to use resource().onClassLoader(classLoader) or resource().onClassLoaderOf(clazz).

Example:

var text = resource()
        .onClassLoader(this.getClass().getClassLoader())
        .withPath("/com/adelean/junit/jupiter", "resource.txt")
        .text();

// or

var text = resource()
        .onClassLoaderOf(this.getClass())
        .withPath("/com/adelean/junit/jupiter", "resource.txt")
        .text();

We can omit the choice of a classloader with resource().withPath(path). In that case the context classloader Thread.currentThread().getContextClassLoader() is used.

3.2.3. Specifying path to resource

Method withPath(path) allows to define the path to the requested resource. The next three notations are equivalents:

// There notations are equivalent
resource().withPath("/com/adelean/junit/jupiter/resource.txt");
resource().withPath("/com/adelean/junit/jupiter", "resource.txt");
resource().withPath("com", "adelean", "junit", "jupiter", "resource.txt");

3.2.4. Read text content

Content of a text resource can be read as String using method text():

var text = resource().withPath("resource.txt").text();

// or with specified charset
var text = resource().withPath("resource.txt").text(StandardCharsets.UTF_8);

3.2.5. Read binary content

Content of a binary resource can be read as byte[] using method bytes():

var fibonacci = resource().withPath("fibonacci.bin").bytes();

3.2.6. Parsing of resource content

In order to parse resources content, need to get one of provided wrappers around that resource. Wrapper can be obtained by calling one of the those methods:

  • asInputStream() - returns wrapper around resource represented as an InputStream.

  • asByteArray() - resource as bytes array.

  • asReader() - resource as Reader. Can also be asReader(Charset charset).

  • asLines() - resource as a stream of text lines. Can also be asLines(Charset charset).

  • asText() - resource as text. Can also be asText(Charset charset).

Those wrappers (except those returned by asLines()) expose four methods that can be used to process content of a resource:

  • parse(Function<I, O> parsingFunction) - parses content of resource using parsingFunction.

  • parseChecked(ThrowingFunction<I, O> parsingFunction) - parses content of resource using parsingFunction that may throw exception.

  • then(Consumer<I> contentConsumer) - processes content of resource using contentConsumer.

  • thenChecked(ThrowingConsumer<I> contentConsumer) - processes content of resource using contentConsumer that may throw exception.

Ignoring uncaught exceptions

Two new functional interfaces are provided by the library: ThrowingFunction and ThrowingConsumer. They are similar to interfaces Function and Consumer from java.util.function but they are able to capture lambdas that may throw a checked exception. Example:

// Instead of this
var dbConnection = resource()
        .withPath("db.properties")
        .asInputStream()
        .parse(inputStream -> {
            try {
                return reader.readValue(inputStream);
            } catch (IOException parsingException) {
                throw new RuntimeException(parsingException);
            }
        });

// We can write this
var dbConnection = resource()
        .withPath("db.properties")
        .asInputStream()
        .parseChecked(reader::readValue);
Read resource line by line

The resource wrapper returned by method asLines() able to process text resource file line by line. It’s practical to parse resources in line based formats, like CSV. Example:

var header = new AtomicReference<String>();
var lines = new ArrayList<String>();

resource()
        .withPath("cities.csv")
        .asLines()
        .onFirstLine(header::set)
        .forEachLine(lines::add);

4. Spring module

@InjectResources module for convenient resource injection in your Spring application.

This module is alternative to ResourceLoader and ResourceUtils from Spring.

4.1. Dependency

Add inject-resources-spring dependency to your project.

Using Maven:

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-spring</artifactId>
    <version>0.2.2</version>
</dependency>

Or using Gradle:

compile group: 'io.hosuaby', name: 'inject-resources-spring', version: '0.2.2'

4.2. Enabling resource injection

To enable resource injection annotation any spring configuration with @EnableResourceInjection.

Example:

 @Configuration
 @EnableResourceInjection
 public class MyConfig {
 }

Then you can use resource annotations to inject content of resources into your beans:

@Component
public class MyBean {

    @BinaryResource("/com/adelean/junit/jupiter/fibonacci.bin") (1)
    private byte[] fibonacciInstanceField;

    @Autowired
    public MyBean(
            @TextResource("/com/adelean/junit/jupiter/resource.txt") (2)
            String text) {
        // ...
    }

    @JsonResource(from = "/com/adelean/junit/jupiter/sponge-bob.json", parserBean = "defaultObjectMapper") (3)
    public void setParsedJson(Person parsedJson) {
        // ...
    }
}
1 Injects binary content into instance field
2 Injects text content into constructor argument
3 Injects JSON parsed as POJO into setter

4.3. Resource annotations

Resource annotations can annotate beans field, constructor argument or setter method that must be injected with content of resource file.

The following sections present those available resource annotations and their usage.

4.3.1. Binary resources

Given a resource /com/adelean/junit/jupiter/fibonacci.bin with binary content:

1 1 2 3 5 8 13 21 34 55 89

Inject the content of that resource using annotation @BinaryResource:

@Component
class MyBean {

    @BinaryResource("/com/adelean/junit/jupiter/fibonacci.bin")
    private byte[] fibonacci;
}

4.3.2. Text resources

Assuming there is a resource /com/adelean/junit/jupiter/resource.txt containing

The quick brown fox jumps over the lazy dog.

Content of that resource can be injected using annotation @TextResource:

@Component
class MyBean {

    @TextResource("/com/adelean/junit/jupiter/resource.txt")
    private String instanceField;
}

4.3.3. Properties resources

Given a properties resource /com/adelean/junit/jupiter/db.properties with content:

db.user=hosuaby
db.password=password
db.url=localhost

These properties can be injected using annotation @PropertiesResource:

@Component
class MyBean {

    @PropertiesResource("/com/adelean/junit/jupiter/db.properties")
    private Properties dbProperties;
}

4.3.4. JSON and JSON Lines resources

Annotations JsonResource and JsonLinesResource can be used to load and parse content of resources in formats JSON and JSON Lines (one JSON document per line) respectively. They rely on popular libraries, Jackson or Gson to parse content into multiple Java types like Map<String, Object>, JsonNode (for Jackson) & JsonElement (for GSON) or to Java POJO.

In order to use those annotations bean of type com.fasterxml.jackson.databind.ObjectMapper from Jackson or com.google.gson.Gson from GSON must be present in application context.

JsonResource and JsonLinesResource have a property parserBean that allows to specify the name of used parser bean in case where there are multiple ObjectMapper or Gson beans in context. If value of that property is empty, the first found or primary bean if those types are used.

Example with Jackson
 @Configuration
 @EnableResourceInjection
 public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper defaultObjectMapper() {
        return new ObjectMapper();
    }

    @Bean
    public ObjectMapper logsObjectMapper() {
        return new ObjectMapper()
                .registerModule(new JavaTimeModule());
    }
 }
@Component
public class MyBean {

    /* JSON resources */
    @JsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    static Map<String, Object> jsonAsMap;

    @JsonResource(from = "/com/adelean/junit/jupiter/sponge-bob.json", parserBean = "defaultObjectMapper")
    JsonNode jsonNode;

    @JsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    Person spongeBob;

    /* JSONL resources */
    @JsonLinesResource(from = "/com/adelean/junit/jupiter/logs.jsonl", parserBean = "logsObjectMapper")
    private Log[] logsAsArray;

    @JsonLinesResource(from = "/com/adelean/junit/jupiter/logs.jsonl", parserBean = "logsObjectMapper")
    private Collection<Log> logsAsCollection;
}
Example with Gson
@Configuration
@EnableResourceInjection
public class GsonConfig {

    @Bean
    @Primary
    public Gson defaultGson() {
        return new Gson();
    }

    @Bean
    public Gson logsGson() {
        return new GsonBuilder()
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
                .create();
    }
}
@Component
public class MyBean {

    /* JSON resources */
    @JsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    private static Map<String, Object> jsonAsMap;

    @JsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    protected JsonElement jsonElement;

    @JsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    Person spongeBob;

    /* JSONL resources */
    @JsonLinesResource(from = "/com/adelean/junit/jupiter/logs.jsonl", parserBean = "logsGson")
    private Log[] logsAsArray;

    @JsonLinesResource(from = "/com/adelean/junit/jupiter/logs.jsonl", parserBean = "logsGson")
    private Collection<Log> logsAsCollection;
}

4.3.5. YAML and YAML documents resources

Annotations YamlResource and YamlDocumentsResource can be used to parse content of resources in formats YAML and YAML documents (multiple YAML documents in the same file separated by three hyphens ---). Bean of type org.yaml.snakeyaml.Yaml from Snakeyaml must be present in application context.

Example:

@Configuration
@EnableResourceInjection
public class YamlConfig {

    @Bean
    @Primary
    public Yaml defaultYaml() {
        return new Yaml();
    }

    @Bean("log-parser")
    public Yaml logYaml() {
        return new Yaml(new Constructor(Log.class));
    }
}
@Component
public class MyBean {

    /* YAML resources */
    @YamlResource("/com/adelean/junit/jupiter/receipt.yml")
    Map<String, Object> receipt;

    @YamlResource("/com/adelean/junit/jupiter/sponge-bob.yaml")
    Person spongeBob;

    /* YAML documents resources */
    @YamlDocumentsResource(from = "/com/adelean/junit/jupiter/stacktrace.yaml", yamlBean = "defaultYaml")
    List<Map<String, Object>> stacktraceAsList;

    @YamlDocumentsResource(from = "/com/adelean/junit/jupiter/stacktrace.yaml", yamlBean = "defaultYaml")
    Map<String, Object>[] stacktraceAsArray;

    @YamlDocumentsResource(from = "/com/adelean/junit/jupiter/logs.yml", yamlBean = "log-parser")
    Log[] logsAsArray;

    @YamlDocumentsResource(from = "/com/adelean/junit/jupiter/logs.yml", yamlBean = "log-parser")
    Collection<Log> logsAsCollection;
}

5. JUnit Jupiter extension

@InjectResources JUnit Jupiter extension gives ability to inject content of resources into JUnit5 tests.

5.1. Dependency

Add inject-resources-core and inject-resources-junit-jupiter dependencies to your project.

Using Maven:

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-core</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-junit-jupiter</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

Or using Gradle:

testCompile group: 'io.hosuaby', name: 'inject-resources-core', version: '0.2.2'
testCompile group: 'io.hosuaby', name: 'inject-resources-junit-jupiter', version: '0.2.2'

5.2. Test extension

In order to use this plugin, we need to add extension to the test class:

@TestWithResources  // add JUnit Jupiter extension
class InjectResourcesTests {
}

5.3. Resource content injection

To inject content of resource files in our tests we need to use one of the @Given* annotations (called resource annotations). Resource annotations can be placed on static class fields, instance fields and parameters of test methods. Injected fields must be non-private and non-final.

@TestWithResources
class InjectResourcesTests {

    @GivenTextResource("/com/adelean/junit/jupiter/resource.txt") (1)
    static String classField;

    @GivenTextResource("/com/adelean/junit/jupiter/resource.txt") (2)
    String instanceField;

    @Test
    public void testWithResources(
            @GivenTextResource("/com/adelean/junit/jupiter/resource.txt") (3)
            String testParameter) {
    }
}
1 Inject into static class field
2 Inject into instance field
3 Inject into test parameter

5.4. Load binary resource

Given a resource /com/adelean/junit/jupiter/fibonacci.bin with binary content:

1 1 2 3 5 8 13 21 34 55 89

Inject the content of that resource using annotation @GivenBinaryResource:

@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);
    }
}

5.5. Load text resource

Assuming there is a resource /com/adelean/junit/jupiter/resource.txt containing

The quick brown fox jumps over the lazy dog.

Content of that resource can be injected into test using annotation @GivenTextResource:

@TestWithResources
class InjectTextResourcesTests {

    @GivenTextResource("/com/adelean/junit/jupiter/resource.txt")
    String instanceField;

    @Test
    public void testInjectTextIntoStringInstanceField() {
        assertThat(instanceField)
                .isEqualTo("The quick brown fox jumps over the lazy dog.");
    }
}

5.6. Load properties resources

Given a properties resource /com/adelean/junit/jupiter/db.properties with content:

db.user=hosuaby
db.password=password
db.url=localhost

These properties can be injected into tests using annotation @GivenPropertiesResource:

@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");
    }
}

5.7. Load and parse JSON

This library simplifies loading and parsing of JSON & JSONL (JSON Lines) files. Native support for Jackson and GSON gives ability to parse JSON content into Map<String, Object>, JsonNode (for Jackson) & JsonElement (for GSON) or to Java POJO.

Use annotation @GivenJsonResource for JSON and @GivenJsonLinesResource for JSONL.

In order to parse JSON/JSONL we need to declare a parser. Parsers are fields or functions that are annotated with @With* annotations.

/* Jackson */
@WithJacksonMapper
ObjectMapper objectMapper = new ObjectMapper();

/* or GSON */
@WithGson
Gson gson = new Gson();

Following subsections explain how to parse JSON/JSONL with Jackson or GSON in detail.

5.7.1. Jackson

In order to parse resources with Jackson both jackson-core & jackson-databind must be present on the Classpath.

To specify which ObjectMapper is used to parse JSON use annotation @WithJacksonMapper:

@WithJacksonMapper
ObjectMapper objectMapper = new ObjectMapper();

Configure your parser as you need:

@WithJacksonMapper
ObjectMapper objectMapper = new ObjectMapper()
        .registerModule(new JavaTimeModule());

Now you can inject content of JSON/JSONL into your tests:

@TestWithResources
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;
}

5.7.2. GSON

To parse JSON/JSONL resources with GSON, com.google.code.gson:gson must be present on Classpath.

Declare GSON object used to parse resources and annotate it with @WithGson:

@WithGson
Gson gson = new Gson();

Now you can inject content of JSON/JSONL into your tests:

@TestWithResources
class TestsWithGson {

    @WithGson
    Gson gson = new GsonBuilder();

    /* JSON */
    @GivenJsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    Map<String, Object> jsonAsMap;

    @GivenJsonResource("/com/adelean/junit/jupiter/sponge-bob.json")
    JsonElement jsonElement;

    @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;
}

5.8. Load and parse YAML

Thanks to native support of Snakeyaml library is able to parse YAML resources files. org.yaml:snakeyaml must be present on Classpath in order to use that feature.

Declare Yaml object used for parsing and annotate it with @WithSnakeYaml:

@WithSnakeYaml
Yaml yaml = new Yaml();

Resource annotations @GivenYamlResource and @GivenYamlDocumentsResource can be now used to parse YAML with single or multiple documents respectively:

@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;
}

Yaml object must be configured to be able to parse documents from multi-document YAML into POJO:

@TestWithResources
class TestsWithYaml {

    /* Assuming we have defined class Log */
    @WithSnakeYaml("log-parser")
    Yaml logParser = new Yaml(new Constructor(Log.class));

    @GivenYamlDocumentsResource(from = "/com/adelean/junit/jupiter/logs.yml", yaml = "log-parser")
    Log[] logsAsArray;
}

5.9. Parsers

This section talks about objects annotated with @With* annotations. Those objects are called parsers. Annotations @With* are called parser annotations. They can annotate class and instance fields or methods of test classes.

// Parser object from field
@WithJacksonMapper
ObjectMapper objectMapper = new ObjectMapper();

// Parser object from function
@WithJacksonMapper("custom-mapper")
ObjectMapper objectMapper() {
    return new ObjectMapper().registerModule(new JavaTimeModule());
}

Parsers can be named or anonymous. Named parsers are useful when some resources require a special configuration of parser.

In following example, resource /com/adelean/junit/jupiter/logs.jsonl is parsed by ObjectMapper named "custom-mapper".

@TestWithResources
class TestsWithNamedParser {

    // Named parser
    @WithJacksonMapper("custom-mapper")
    ObjectMapper objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule());

    // JSONL resource parser by parser named 'custom-mapper'
    @GivenJsonLinesResource(
        from = "/com/adelean/junit/jupiter/logs.jsonl",
        jacksonMapper = "custom-mapper")
    Collection<Log> logsAsCollection;
}

5.9.1. Parser scopes

By default, parsers are scoped to test class where they were defined. If a test class inherits from another class, parsers defined in superclass can be used in subclass:

abstract class SuperClassWithParser {

    @WithJacksonMapper("custom-mapper")
    ObjectMapper objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule());
}

@TestWithResources
class TestsSubclass extends SuperClassWithParser {

    @GivenJsonLinesResource(
        from = "/com/adelean/junit/jupiter/logs.jsonl",
        jacksonMapper = "custom-mapper")
    Collection<Log> logsAsCollection;
}

5.9.2. Tests advice

It is possible to define a parser that can be used by all tests on the Classpath by creating a public final class annotated with @TestsAdvice:

@TestsAdvice
public final class GlobalJacksonMapper {

    @WithJacksonMapper("custom-mapper")
    ObjectMapper objectMapper() {
        return new ObjectMapper().registerModule(new JavaTimeModule());
    }
}

@TestWithResources
class TestsWithJson {

    @GivenJsonLinesResource(
        from = "/com/adelean/junit/jupiter/logs.jsonl",
        jacksonMapper = "custom-mapper")
    Collection<Log> logsAsCollection;
}

6. JUnit 4 (Vintage) extension

@InjectResources JUnit 4 (Vintage) extension simplifies access to content of resources in your JUnit tests.

6.1. Dependency

Add inject-resources-core and inject-resources-junit-vintage dependencies to your project.

Using Maven:

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-core</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.hosuaby</groupId>
    <artifactId>inject-resources-junit-vintage</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

Or using Gradle:

testCompile group: 'io.hosuaby', name: 'inject-resources-core', version: '0.2.2'
testCompile group: 'io.hosuaby', name: 'inject-resources-junit-vintage', version: '0.2.2'

6.2. Resource rules

This library inject-resources-junit-vintage provides set of rules that simplify access to content of resources. Those rules are accessible through static method GivenResource.givenResource(). It’s recommended to statically import this method in test classes and use it as chained builder for resource rules.

import static com.adelean.inject.resources.junit.vintage.GivenResource.givenResource;

Then resource rules can be built using chained builder:

@Rule
public JsonResource<Person> spongeBob = givenResource()
        .json("/com/adelean/junit/jupiter/sponge-bob.json")
        .withCharset(StandardCharsets.UTF_8)
        .parseWith(new ObjectMapper());

Following subsections show rules for different types of resources.

6.2.1. Binary resource rule

BinaryResource rule loads content of binary resource.

Given a resource /com/adelean/junit/jupiter/fibonacci.bin with binary content:

1 1 2 3 5 8 13 21 34 55 89

Its content can be accessed as following:

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);
    }
}

6.2.2. Text resource rule

TextResource loads content of text resource.

Assuming there is a resource /com/adelean/junit/jupiter/resource.txt containing

The quick brown fox jumps over the lazy dog.

Its content can be accessed as following:

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.");
    }
}

6.2.3. Properties resource rule

PropertiesResource loads content of java properties resource.

Given a properties resource /com/adelean/junit/jupiter/db.properties with content:

db.user=hosuaby
db.password=password
db.url=localhost

Its content can be accessed as following:

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");
    }
}

6.2.4. JSON and JSON Lines resource rules

Rules JsonResource and JsonLinesResource can be used to parse content of resources in formats JSON and JSON Lines (one JSON document per line) respectively. They rely on popular libraries, Jackson or Gson to parse content into multiple Java types like Map<String, Object>, JsonNode (for Jackson) & JsonElement (for GSON) or to Java POJO.

In order to use those rules Jackson (modules jackson-core & jackson-databind) or GSON (module com.google.code.gson:gson) must be present on Classpath.

json() and jsonLines() rule builders require parser object provided using method parseWith(Object parser).

Example:

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 with Gson */
    @Rule
    public JsonLinesResource<Collection<Log>> logsAsCollection = givenResource()
            .jsonLines("/com/adelean/junit/jupiter/logs.jsonl")
            .parseWith(new Gson());
}

6.2.5. YAML and YAML documents resource rules

Rules YamlResource and YamlDocumentsResource can be used to parse content of resources in formats YAML and YAML documents (multiple YAML documents in the same file separated by three hyphens ---). org.yaml:snakeyaml must be present on Classpath in order to use those rules.

yaml() and yamlDocument() rule builders requires to specify org.yaml.snakeyaml.Yaml (Snakeyaml) parser object using method parseWith(Yaml yaml).

Example:

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)));
}