I am running into trouble with JUnit 5 (5.0 or 5.1) and custom extension.
We are using service loader to load all implementations which then modify how our extension is bootstrapped. These implementations can be loaded just once, so I was thinking of using ExtensionContext.Store
and placing it there. Every subsequent test instance would then just load it from Store
instead of via service loader.
Now, I am even aware of the hierarchical context structure and I know that there is some "root" context which you can get through ExtensionContext.getRoot()
. But this "root" context (instance of JupiterEngineExtensionContext
) isn't really root - there is different one for every test instance.
Say you have FooTest
and BarTest
, then printing out getRoot()
for each of them yields:
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext@1f9e9475
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext@6c3708b3
And hence trying to retrieve previously stored information from Store
fails.
ClassExtensionContext
and JupiterEngineExtensionContext
pretty blurred.Here is a (very) simplified version of how I tried working with the store (cutting out all other information basically). I also added some System.out.print()
calls to underline what I am seeing. Executing this extension on two test classes results in what I described above:
public class MyExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
System.out.println(context.getRoot());
if (context.getRoot().getStore(Namespace.create(MyExtension.class)).get("someIdentifier", String.class) == null) {
context.getRoot().getStore(Namespace.create(MyExtension.class)).put("someIdentifier", "SomeFooString");
} else {
// this is never executed
System.out.println("Found it, no need to store anything again!");
}
}
}
EDIT: Here is a minimal project on GH(link), run by mvn clean install
, which displays the behaviour I see.
I just copied your MyExtension
verbatim (i.e., with zero changes) and ran both FooTest
and BarTest
.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MyExtension.class)
class FooTest {
@Test
void test() {
}
}
and
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MyExtension.class)
class BarTest {
@Test
void test() {
}
}
And the result is:
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext@2280cdac
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext@2280cdac
Found it, no need to store anything again!
Thus, getRoot()
works as documented.
The only explanation for why you see two different roots is that you must be executing the tests in different processes.
Please keep in mind that the root ExtensionContext
instance is bound to the current execution of your test suite.
So if you run FooTest
and BarTest
one after the other in an IDE, that will actually result in two "test suites" with different roots. The same is true if you configure your build tool to fork between test classes.
Whereas, if you execute both test classes together in a single "test suite" (e.g., by telling your IDE to run all tests in the same package or same source tree) you will then see that there is one root like in the output I provided above.
Note, however, that there was an issue with the junit-platform-surefire-provider
prior to version 1.0.3, whereby the provider launched the JUnit Platform for each test class. This would give the appearance of forking even though Surefire did not actually start a new JVM process. For details, see https://github.com/junit-team/junit5/pull/1137.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With