JUnit 5 is a great framework but part of it are still not fluent (mainly because we don't use it often). An interesting thing is that you can cache some state in a "global per suite" store. It is very useful to run something once only.

Common examples are:

  • Run some bytecode enhancer,
  • Initialize some database,
  • Initialize some mocks.

Switching from a per class to a per suite model will save a lot of time when you will be building the whole project.

All the trick comes from the fact JUnit 5 suites (which are actually a set of test, for instance if you run multiple tests in Intellij you will have this as well) has an ExtensionContext which is the parent of any class extension context (same as the parent of a method context is the class context).

Therefore to run only once any code you can just put a marker in this store:

public class MyExtension implements BeforeAllCallback {
    private static final ExtensionContext.Namespace NAMESPACE =
        ExtensionContext.Namespace.create(MyExtension.class);

    @Override
    public void beforeAll(final ExtensionContext context) {
        context.getParent()
            .map(e -> e.getStore(NAMESPACE).getOrComputeIfAbsent(MyMarker.class, k -> {
              doInit();
              return new MyMarker();
            }));
    }
}

Indeed, you can just use a Boolean and don't need MyMarker class. It is however common to put the initializer class which can store some state. For instance:

public class MyExtension implements BeforeAllCallback {
    private static final ExtensionContext.Namespace NAMESPACE =
        ExtensionContext.Namespace.create(MyExtension.class);

    @Override
    public void beforeAll(final ExtensionContext context) {
        context.getParent()
            .map(e -> e.getStore(NAMESPACE).getOrComputeIfAbsent(MyInit.class, k -> {
              final MyInit init = new MyInit().run();
              return init;
            }));
    }
}

Then, with the same extension you can also enable to inject MyInit as a parameter or field. Very common if MyInit holds some shared logic (like a client on a dynamic mock for example).

From the same author:

In the same category: