Configuring deterministic execution order for multiple @ExtendWith annotations in JUnit 5
0 reputation · 24 Jul 2025, 20:03 UTC
0 reputation · 24 Jul 2025, 20:03 UTC
When a test class is annotated with several @ExtendWith entries, each registering extensions that implement lifecycle callbacks such as BeforeEachCallback or AfterEachCallback, the JUnit Platform does not guarantee a deterministic invocation order unless the extensions also implement Orderable or are declared via @RegisterExtension with an explicit sequence. This unspecified order is noted as an implementation detail that may change between JVMs, classloader layouts, or JUnit releases, which can produce intermittent failures that are hard to trace from deployment logs.
The goal is to achieve a stable, repeatable order for these extensions without requiring each extension to implement Orderable or consolidating logic into a single extension, and without relying on the current annotation declaration order, which is not documented as a guarantee.
Is there a mechanism to declare a global ordering rule for @ExtendWith annotations? Can the JUnit Platform be configured to treat annotation declaration order as a stable default? Does setting a specific junit-platform.properties flag enforce deterministic extension invocation?
JUnit 5 does not provide a flag or configuration that makes the declaration order of @ExtendWith annotations deterministic. The platform discovers extensions via reflection and invokes their lifecycle callbacks in an unspecified order that may vary between JVMs, classloaders, or JUnit releases.
org.junit.jupiter.api.extension.Orderable interface (or be declared as a @RegisterExtension field with an @Order annotation).junit-platform.properties property that forces annotation‑order‑based sorting.Orderable and delegates to the wrapped extensions in the sequence you choose.BeforeEachCallback, AfterEachCallback (or any other lifecycle interfaces you need) and Orderable.getOrder() to return a value that positions the wrapper relative to any other ordered extensions you may have.@ExtendWith(YourWrapper.class) (remove the individual @ExtendWith annotations for the wrapped extensions).public class OrderedExtensionWrapper implements BeforeEachCallback, AfterEachCallback, Orderable { private final List<BeforeEachCallback> befores = new ArrayList<>(); private final List<AfterEachCallback> afters = new ArrayList<>(); private final int order; public OrderedExtensionWrapper(int order, Extension... extensions) { this.order = order; for (Extension e : extensions) { if (e instanceof BeforeEachCallback) befores.add((BeforeEachCallback) e); if (e instanceof AfterEachCallback) afters.add((AfterEachCallback) e); } } @Override public int getOrder() { return order; } @Override public void beforeEach(ExtensionContext context) throws Exception { for (BeforeEachCallback cb : befores) { cb.beforeEach(context); } } @Override public void afterEach(ExtensionContext context) throws Exception { for (AfterEachCallback cb : afters) { cb.afterEach(context); } } } // Usage in a test class @ExtendWith(OrderedExtensionWrapper.class) public class MyTest { @Test void something() { /* … */ } }When to ask for a diagnostic detail
If you are using a JUnit 5 version prior to 5.7 (where
Orderablewas introduced), the wrapper approach is the only viable path; otherwise, simply implementOrderabledirectly on each extension. Knowing your exact JUnit version would confirm whetherOrderableis available, but the wrapper works on all 5.x releases.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.