JUnit 5 extensions: Resource injection

Marcus • May 23, 2020

An oil processing plant, overshadowed by clouds.

Now that we looked at how field injection can provide us with useful ways of setting up test classes with parameters, there is still one last piece of the JUnit 5 lifecycle missing: Getting custom resources wired up.

JUnit Extension Contexts

Now whereas getting parameters was trivially accomplished by simply fetching them again each time they’re needed in any JUnit 5 callback, that’s a questionable practice to use with resources.

If we assume just for a moment that the resource have a cost associated to either creating or managing them, there’s a good chance you just want it to be associated once for a test class. If you acquire a resource in each beforeEach or beforeTestExecution, that gets expensive very quickly.

The primary way of storing data associated with an extension is the ExtensionContext.Store.

JUnit has a hierarchy associated with extensions: Each beforeAll/afterAll pair has a context, and under that each beforeEach/afterEach has a child context. If you put things into the store in beforeAll, you can easily retrieve them later during beforeEach.

Different contexts exist, and we’ve seen one previously: The ParameterResolver has a ParameterContext to describe the parameter of a test method, and which we used to filter out parameters to resolve.

Closeable Resources

If porting code directly from JUnit 4 to here, the perfectly acceptable way to release a resource at a later point in time would be to handle that part in the afterAll callback.

JUnit 5 brings along an interface to implement if you’re specificially talking about this concept: ExtensionContext.Store.CloseableResource. As the name and the documentation suggest, this is only applicable for things you store within a Store. Let’s look at a simple resource that we allocate manually & that is destroyed automatically:

public class ExampleResource
  implements ExtensionContext.Store.CloseableResource {

  private static final Logger LOG =
    LoggerFactory.getLogger(ExampleResource.class);

  /** Used to distinguish resources from one another,
    * just for demonstration */
  private final UUID resourceId = UUID.randomUUID();

  public ExampleResource() {
    LOG.debug("Creating resource {}", resourceId);
  }

  public UUID getResourceId() {
    return resourceId;
  }

  @Override
  public void close() {
    LOG.debug("Closing resource {}", resourceId);
  }
}

And here’s the extension that goes along with it:

public class StoringExtension
  implements BeforeAllCallback, ParameterResolver {

  private static final ExtensionContext.Namespace NAMESPACE =
    ExtensionContext.Namespace.create(StoringExtension.class);
  private static final String KEY_EXAMPLE_RESOURCE =
    "example-resource";

  @Override
  public void beforeAll(ExtensionContext context) {
    context.getStore(NAMESPACE)
      .put(KEY_EXAMPLE_RESOURCE, new ExampleResource());
  }

  @Override
  public boolean supportsParameter(
      ParameterContext parameterContext,
      ExtensionContext extensionContext) {
    return parameterContext.getParameter().getType() ==
      ExampleResource.class;
  }

  @Override
  public ExampleResource resolveParameter(
      ParameterContext parameterContext,
      ExtensionContext extensionContext) {
    return extensionContext.getStore(NAMESPACE)
      .get(KEY_EXAMPLE_RESOURCE, ExampleResource.class);
  }
}

By just annotating a test with our new extension, the following happens:

That’s a limited example, to be sure, and the following are suggestions on what you could do:

In particular, point #3 on our list could be easily achieved with what you already know: Implement a beforeEach callback and you’re done. But we can do better.

Instance Factory

If the class itself is very strongly bound to having a resource of your type accessible, a TestInstanceFactory might be what you need: You have full control over how test instances are created, be it calling a parameterized constructor directly or a factory method.

Depending on whether you have a @TestInstance and its value, this can either be called once per test method or once per class. The ExtensionContext stays the same between each call if configured per test method, which is why this example uses getOrComputeIfAbsent for retrieving the associated resource[1].

Here’s how that looks:

public class FactoryResourceExtension
  implements TestInstanceFactory {

  private static final ExtensionContext.Namespace NAMESPACE =
    ExtensionContext.Namespace.create(StoringExtension.class);
  private static final String KEY_EXAMPLE_RESOURCE =
    "example-resource";

  @Override
  public Object createTestInstance(
      TestInstanceFactoryContext factoryContext,
      ExtensionContext extensionContext)
      throws TestInstantiationException {

    ExampleResource resource = extensionContext.getStore(NAMESPACE)
      .getOrComputeIfAbsent(KEY_EXAMPLE_RESOURCE,
        k -> new ExampleResource(), ExampleResource.class);

    try {
      return factoryContext.getTestClass()
        .getDeclaredConstructor(ExampleResource.class)
        .newInstance(resource);
    } catch (InstantiationException | IllegalAccessException |
      InvocationTargetException | NoSuchMethodException e) {
      throw new TestInstantiationException(
        "Could not create test class: " + e.getMessage(), e);
    }
  }
}

And things would all be well if that was it, but one very important caveat should be apparent from the code: Only one TestInstanceFactory can exist for one test class. Unless a very good specific reason exists for you to implement one, other options are preferable.

As far as I know, this is the only way to set final fields within a class - by setting them in the constructor.

TestInstancePostProcessor

Contrary to the factory defined in the previous paragraph, the sole purpose of a TestInstancePostProcessor is to do stuff after an instance has been created. The code is, in fact, very very similar to the code above. Instead of using the FactoryContext, the created instance is passed around:

public class PostProcessingExtension
  implements TestInstancePostProcessor {

  private static final ExtensionContext.Namespace NAMESPACE =
    ExtensionContext.Namespace.create(StoringExtension.class);
  private static final String KEY_EXAMPLE_RESOURCE =
    "example-resource";

  @Override
  public void postProcessTestInstance(
      Object testInstance,
      ExtensionContext extensionContext)
      throws IllegalAccessException {

    ExampleResource resource = extensionContext.getStore(NAMESPACE)
      .getOrComputeIfAbsent(KEY_EXAMPLE_RESOURCE,
        k -> new ExampleResource(), ExampleResource.class);

    List<Field> fieldsToInject = ReflectionSupport.findFields(
      testInstance.getClass(),
      field -> field.getType() == ExampleResource.class,
      HierarchyTraversalMode.TOP_DOWN);
    for (Field field : fieldsToInject) {
      field.set(testInstance, resource);
    }
  }
}

While we’re using the resource type in this particular example, I would strongly recommend defining an explicit annotation for your resource fields to allow for troubleshooting if something goes wrong: You could make it obvious the wrong type is being used instead of leaving it null, for example.

But other than that, this is slightly better than using beforeEach here, since it obeys the lifecycle of the test class. Aside from slightly reducing reflection usage if @TestInstance is set to PER_CLASS, this is virtually identical.

Why you should not inject parameters this way

Our previously shown way of retrieving parameters relies very heavily on reflection, and the types passed around here are a bit different. Since the operations outlined above work on the class-level, they do not have a strong reference to the method being called.

There’s a class-specific descriptor, not the TestMethodTestDescriptor we previously used. If tried hard enough, you could possibly extract the context you’re looking for, but that’s messy for a variety of reasons.

The current implementation, when coupled with the PER_METHOD test instance creation, builds the full context on the fly. You need to build a diff between the old and the new deeply nested context to find out not only what methods a class has, what parameters they could possibly have, but also which set of parameters is the one for the current execution.

Using a single test instance per class makes it nigh impossible to inject any parameter correctly.

What I would recommend

So far we’ve discussed a few different approaches: The following are all viable ways of acquiring resources once per test class:

My personal preference is a combination of approaches. This allows having the developer who is using your extension to decide which way they want to access your resources:

With this done, you can declare fields for the resource if you want it to be available globally, or define them as parameters instead.

With that said, a full example for everything outlined in the article can be found on GitHub:

View Source Code @ GitHub.


  1. If you have a BeforeAllCallback implemented, this is called before either TestInstanceFactory or TestInstancePostProcessor. You can replace getComputeOrDefault with get.
}