Java annotations are quite useful in many areas such as introspection, Aspect-Oriented Programming, etc. Spring Framework uses quite a lot of annotations. My team has been using annotations extensively in our projects and one particular use case is using annotations together with JUnit 5 ParameterResolver to inject test resources.
We will start by creating a custom annotation:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FileResource {
/**
* @return the path to the resource.
*/
String value();
/**
* @return the type of value that should be decoded from the resource
*/
Class<?> type() default String.class;
}
Then we would need to implement a custom ParameterResolver using the annotation.
public class FileResourceParameterResolver implements ParameterResolver {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
FileResource fileResource = parameterContext
.findAnnotation(FileResource.class).orElse(null);
return fileResource != null &&
fileResource.type().isAssignableFrom(
parameterContext.getParameter().getType());
}
@SneakyThrows
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
var fileResource = parameterContext
.findAnnotation(FileResource.class)
.orElseThrow(IllegalArgumentException::new);
String path = fileResource.value();
InputStream content = getClass().getClassLoader().getResourceAsStream(path);
if (content == null) {
throw new ParameterResolutionException("Resource not found: " + path);
}
if (fileResource.type() == String.class) {
return IOUtils.toString(content, StandardCharsets.UTF_8);
}
return MAPPER.readValue(content, parameterContext.getParameter().getType());
}
}
Now we have our custom ParameterResolver ready to use. In our test, what we need to do is the following:
@ExtendWith({ FileResourceParameterResolver.class })
class SomeTest {
@Test
void importResourceTest(@FileResource("some/json/file.json") String inputJson) {
// use content below in test
}
}