Easily inject file based resources into your jupiter tests
See Releases for the latest maven/gradle coordinates and version
If you are using at least junit v5.8 then you don't need to do anything to enable the extension, you can jump straight to Injecting resources
I'm using junit < v5.8
You can enable the extension on a per-test class basis using the @ExtendWith
annotation to register
the ResourcesExtension
like so:
@ExtendWith(ResourcesExtension.class)
class MyTests {
// ...
}
Alternatively the extension also advertises itself for automatic registration, providing you have enabled
it via setting the property junit.jupiter.extensions.autodetection.enabled
to true
. How you do this
will differ depending on your build tool.
Resources can be injected into tests through parameters on test methods, or non-static fields in test classes. A number
of different types can be used which are listed here. The parameter which needs to be populated must be
annotated with the @TestResource
annotation, which configures the path to the resource
class MyTests {
@Test
void myTest(@TestResource("myFile.txt") String myFile) {
// `myFile` will contain the contents of myFile.txt
}
@Test
void myOtherTest(@TestResource("myFile.txt") InputStream inputStream) {
// `inputStream` is reading the contents of myFile.txt
}
@TestResource("myFile.txt") String field;
@Test
void testFromField() {
// `field` will contain the contents of myFile.txt
}
}
For some types, the charset can make a difference and so this can also be specified on the annotation alongside the path. By default, the system default charset will be used.
class MyTests {
@Test
void myTest(@TestResource(value = "myFile.txt", charset = "UTF-8") String myFile) {
// `myFile` will contain the contents of myFile.txt, read using UTF-8
}
}
Not all types support charset, for example when reading into a byte[]
. If you specify a charset on an unsupported type
then the charset will be ignored.
By default, resources are loaded from the current test class path. For example, if you are writing a test in a class
named com.example.SomeTests
and use the annotation @TestResource("file.txt")
then the resource will be loaded from
/com/example/file.txt
.
You can use a /
at the start of your filename to read from the root of the classpath instead, for example
@TestResource("/file.txt")
.
You can also make use of the @TestResourceDirectory
annotation to set a default directory to save specifying it
on every parameter. This annotation can be specified on a test field, parameter, method, class, parent class or package
(in package-info.java
). The first annotation found in this order will be used to determine the directory.
package com.example;
@TestResourceDirectory("subdir")
class SomeTests {
@Test
void example1(@TestResource("file.txt") String file) {
// /com/example/subdir/file.txt
}
@Test
void example2(@TestResource("/file.txt") String file) {
// /file.txt
}
@Test
@TestResourceDirectory("other")
void example3(@TestResource("file.txt") String file) {
// /com/example/other/file.txt
}
@Test
@TestResourceDirectory("/")
void example4(@TestResource("file.txt") String file) {
// /file.txt
}
@Test
void example5(
@TestResource("one.txt") String one,
@TestResourceDirectory("/other") @TestResource("two.txt") String two) {
// /com/example/subdir/one.txt
// /other/two.txt
}
}
A built-in type, ResourceInfo
can also be injected, this type provides some information about the resource itself, and
also allows you to re-resolve into other types later in the test.
package com.example;
class MyTests {
@Test
void testResourceInfo(@TestResource("file.txt") ResourceInfo info) {
var name = info.getName(); // "file.txt"
var fullName = info.getFullName(); // "com/example/file.txt"
// content of file.txt as if it was injected as a string in the first place
var content = info.resolveTo(String.class);
// an InputStream reading the content of file.txt
try (var is = info.resolveTo(InputStream.class)) {
is.read();
} // remember to close!
}
}
Note that if you call resolveTo
for a closeable type, it's your responsibility to close it.
The following types are supported, however you can also add your own!
Type | Charset Supported? |
---|---|
byte[] | No |
Byte[] | No |
ByteArrayInputStream | No |
char[] | Yes |
CharSequence | Yes |
Character[] | Yes |
File | No |
InputStream | No |
JarFile | No |
Path | No |
ResourceInfo | Yes* |
Scanner | Yes |
String | Yes |
StringBuffer | Yes |
StringBuilder | Yes |
StringReader | Yes |
URI | No |
URL | No |
ZipFile | Yes |
*ResourceInfo passes charset information down when calling ResouceInfo::resolveTo(T)
Each type is loaded using an implementation of a ResourceResolver<>
. Implementations are loaded automatically
via ServiceLoader by creating a file at the path META-INF/services/com.testingsyndicate.jupiter.extensions.resources.ResourceResolver
and then adding a line with the fully qualified class name of your implementation. Once you've done this just use
it like any of the built-in ones!
You should implement the doResolve
method in your implementation. When calling the super constructor, specify
the type this resolver supports.
If you think your resolver will be useful for others (i.e. it's for a built-in java type), then feel free to raise a pull request to add it to the library!
Thanks to @chaosmi for the inspiration!