-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lifecycle callback methods in enclosing class are invoked with ExtensionContext for test executing in @Nested test class #1332
Comments
In some test code that I have locally, I have modified one of our test parameter resolvers as follows. public class CustomTypeParameterResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
assertCorrectExtensionContext(parameterContext, extensionContext);
return parameterContext.getParameter().getType().equals(CustomType.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
assertCorrectExtensionContext(parameterContext, extensionContext);
return new CustomType();
}
private static void assertCorrectExtensionContext(ParameterContext parameterContext,
ExtensionContext extensionContext) {
// Ensure that this ParameterResolver is being invoked in the correct ExtensionContext
assertEquals(parameterContext.getDeclaringExecutable().getDeclaringClass(),
extensionContext.getRequiredTestClass());
}
} With the status quo in Jupiter, use of the above resolver fails as outlined in this issue's description. Here we can see that the declaring class for the current method (e.g., a If one wished to argue that this is not a bug, I suppose one could argue that a I could possibly change the behavior of the More importantly, the behavior described in this issue is in direct contrast to the behavior for @junit-team/junit-lambda, Thoughts? |
@marcphilipp, thanks for adding the "team discussion" label! I was just about to do that. ;-) |
FYI: I updated this issue's overview and analysis. |
Regardless of the fate of this issue, the Javadoc for
... is not specific about what "current" means with regard to extensions being applied within a One could infer that the current test class is the currently executing nested test class, but I think we should at least aim to be more explicit in the documentation with regard to |
Actually, I take that back. The "declaring class" of the method may be a superclass or interface. So that is not a viable solution. |
You could check the |
Well.... in case you mean checking the class for the value returned from Otherwise, wouldn't that imply that we have a bug in the state of the supplied |
Oh wait... I guess you actually meant Mea culpa. 😇 |
So, yeah, I guess that would work for a But... it wouldn't help for a static |
In any case, I'm slowly starting to think that this is perhaps not a bug per se but rather just "the nature of the beast"... even if it's sometimes not that intuitive. |
In the end, it might just be that extensions have to provide their own support for configuration "inheritance" within nested class structures to overcome situations like the one described in the linked Spring JIRA issue. But let's have that offline discussion we talked about anyway, just to hash things out. 😉 |
@sbrannen, I would also like to "pin" my thoughts into this issue here. First of all I'd like to divide this issue into two cases:
If we forget about With Having said that, I really think there is some kind of "bug" here in JUnit Jupiter, because the Still, this would mean a breaking change, and we have to discuss the effect of this change... |
Just so we’re on the same page, here are a couple observations/clarifications about the current behavior (which should actually be in the Javadoc or User Guide). The For example, let's consider the following test class: class A {
@BeforeEach void setup(Object x) {}
@Test void a() {}
@Nested class B {
@Test void b() {}
}
} There are five
A
Why isn’t it always called with the Now, if I understood it correctly, Spring’s requirement is to get access to the test class In the simple example above, one could call However, I’m wondering whether the Spring extension couldn’t just store a reference to the application context in the @sbrannen Would it be possible to implement it this way? |
I do not share this though. Test hierarchies are more or less a possibility for grouping and scoping tests and the variables used. On the opposite, the behavior described above is what I would expect from a real class hierarchy, i.e. when the methods are inherited onto the context the test are executed in. This is the primary difference between test class inheritance and test class hierarchies:
Referring to your example above, in a hierarchy, everything is defined on a level (refer to a execution stack) and lives on that level (like a local stack variable). Therefore, the Having said that, I think we really should modify the way the extensions are called today. If the In the case of the Does that make it a bit more clear? Otherwise, just let us talk... |
Team Decision: the team agrees that the introduction of such a method is the bare minimum that should be done in order to provide extension authors enough information to make appropriate decisions. However, how that method should be named is up for debate, and the team agrees that |
I did a bit of brainstrorming and came up with an idea for providing further information to extension authors -- in case we decided the proposed new method is insufficient. We could introduce a new Optional<NestingContext> getNestingContext(); It would likely be The
The list of classes supplied in the last method could of course be determined iteratively using reflection by starting with the So.... just "food for thought". 😉 |
Not sure if this falls in the same category as the described issue, but mocking around with Nested tests, i observed that a defined BeforeAllCallback is called twice when using Simplified example: I have defined an Extension that starts a Wiremock-server (with a fixed port), using an overidden beforAll from the BeforeAllCallback interface. Extending my testclass with this Extension and staring the test will result in an So it looks like the beforeAll is runned twice; once for the encapsulating class, and once more for the inner (nested) class. Does this confirm
|
@Klaboe You're describing the expected behavior. If you want to start it only once, you can do sth. like this: import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
public class WireMockExtension extends BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) {
context.getStore(Namespace.create(WireMockExtension.class))
.getOrComputeIfAbsent("server", key -> new StartedWireMockServer());
}
static class StartedWireMockServer extends CloseableResource {
private final WireMockServer server;
public StartedWireMockServer() {
server = new WireMockServer();
server.start();
}
@Override
public void close() {
server.stop();
}
}
} |
Note that the proposal in #1332 (comment) is closely related to the |
This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. Thank you for your contribution. |
This issue has been automatically closed due to inactivity. If you have a good use case for this feature, please feel free to reopen the issue. |
Overview
A comment in SPR-15366 has made me aware of a potential bug in JUnit Jupiter's handling of
@BeforeEach
methods with regard to theExtensionContext
supplied to aParameterResolver
registered for an enclosing test class when executing a test method within a@Nested
test class.Specifically, Jupiter invokes a
@BeforeEach
method in the enclosing class using theExtensionContext
for a@Test
method in a@Nested
class. The same may be true for other lifecycle callback methods as well, but I have not investigated that.Analysis
It would appear that the cause of this behavior is due to a combination of how
ClassTestDescriptor.invokeMethodInExtensionContext(Method, ExtensionContext, ExtensionRegistry)
andTestMethodTestDescriptor.invokeBeforeEachMethods(JupiterEngineExecutionContext)
operate. The latter invokes each synthesizedBeforeEachMethodAdapter
using theExtensionContext
for the current test method instead of supplying theExtensionContext
for the context in which the method is declared. Consequently, if aParameterResolver
is asked to resolve a parameter for a@BeforeEach
method in an enclosing class and the resolver needs the test class (or information tied to the test class via annotations) to resolve the parameter, then the resolver gets the test class for the currently executing@Nested
test class when invokingextensionContext.getRequiredTestClass()
.However, this behavior does not appear to apply to the invocation of
TestInstancePostProcessor
implementations as can be seen inTestInstancePostProcessorTests
.Similarly, the behavior is different for a
ParameterResolver
applied to the constructor of an enclosing class for the currently executing@Nested
test class.Deliverables
@BeforeAll
@AfterAll
@BeforeEach
@AfterEach
ExtensionContext
.The text was updated successfully, but these errors were encountered: