-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
I am currently writing a small extension which implements ParameterResolver to provide a few parameters which need to be shut down after the test. After reading the documentation, my first intuition was to write the extension like this:
final class MyExtension implements ParameterResolver {
private static final Namespace NAMESPACE = Namespace.create(MyExtension.class);
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == Parameter.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return extensionContext
.getStore(NAMESPACE)
.getOrComputeIfAbsent(ParameterWrapper.class)
.parameter;
}
private static final class ParameterWrapper implements CloseableResource {
private final Parameter parameter;
private ParameterWrapper() {
this.parameter = new Parameter();
}
@Override
public void close() {
parameter.close();
}
}
}The problem with that solution is that the ExtensionContext is scoped for the whole test class when resolving arguments for the test class constructor, although the test uses TestInstance.Lifecycle.PER_METHOD. As a consequence, my example above will use the same parameter for all tests, instead of creating a new one per test. The documentation does not describe a lot of details about the lifecycle of each ExtensionContext, therefore I cannot declare it as a bug, but this behavior is very unexpected in my opinion. Since the callbacks are on the level of the individual tests, I expected the ExtensionContext to be scoped to the test as well. The same problem applies to TestInstancePreConstructCallback. I haven't tested TestInstanceFactory, but I guess it is also affected.
The obvious workaround is to also add a TestInstancePreDestroyCallback which closes the parameter and removes it from the store. Note that the implementation of that method is not trivial because in contrast to the methods above, this callback receives a child store which is scoped to the test. Unfortunately, this means that calling remove on the given store directly instead of iterating over the parents would not work.
@Override
public void preDestroyTestInstance(ExtensionContext context) {
ParameterWrapper wrapper = null;
do {
wrapper = context.getStore(NAMESPACE).remove(ParameterWrapper.class, ParameterWrapper.class);
context = context.getParent().orElse(null);
} while (context != null && wrapper == null);
if (wrapper != null) wrapper.close();
}Deliverables
- Test-scoped
ExtensionContextforInvocationInterceptor.interceptTestClassConstructor(…) - Test-scoped
ExtensionContextforParameterResolverwhile resolving constructor parameters - Test-scoped
ExtensionContextforTestInstancePreConstructCallback - Test-scoped
ExtensionContextforTestInstancePostProcessor - Test-scoped
ExtensionContextforTestInstanceFactory - Consider Expose test method to test instance constructor extensions #3670 in the implementation
-- or --
- Documentation of the limitation in the Javadoc of the interfaces or callbacks.
Related issues
Metadata
Metadata
Assignees
Type
Projects
Status