Parameter Resolution
ParameterResolver defines the Extension API for dynamically resolving parameters at
runtime.
If a test class constructor, test method, or lifecycle method (see
Definitions) declares a parameter, the parameter must be resolved at
runtime by a ParameterResolver. A ParameterResolver can either be built-in (see
TestInfoParameterResolver) or registered by the user.
Generally speaking, parameters may be resolved by name, type, annotation, or any
combination thereof.
If you wish to implement a custom ParameterResolver that resolves parameters based
solely on the type of the parameter, you may find it convenient to extend the
TypeBasedParameterResolver which serves as a generic adapter for such use cases.
For concrete examples, consult the source code for CustomTypeParameterResolver,
CustomAnnotationParameterResolver, and MapOfListsTypeBasedParameterResolver.
|
Due to a bug in the byte code generated by The
|
|
Accessing the test-scoped
ExtensionContextYou may override the |
|
Parameter resolution for methods called from extensions
Other extensions can also leverage registered |
Parameter Conflicts
If multiple implementations of ParameterResolver that support the same type are
registered for a test, a ParameterResolutionException will be thrown, with a
message to indicate that competing resolvers have been discovered. See the following
example:
public class ParameterResolverConflictDemo {
@Test
@ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class })
void testInt(int i) {
// Test will not run due to ParameterResolutionException
assertEquals(1, i);
}
static class FirstIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == int.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 1;
}
}
static class SecondIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == int.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 2;
}
}
}
If the conflicting ParameterResolver implementations are applied to different test
methods as shown in the following example, no conflict occurs.
public class ParameterResolverNoConflictDemo {
@Test
@ExtendWith(FirstIntegerResolver.class)
void firstResolution(int i) {
assertEquals(1, i);
}
@Test
@ExtendWith(SecondIntegerResolver.class)
void secondResolution(int i) {
assertEquals(2, i);
}
static class FirstIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == int.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 1;
}
}
static class SecondIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == int.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 2;
}
}
}
If the conflicting ParameterResolver implementations need to be applied to the same test
method, you can implement a custom type or custom annotation as illustrated by
CustomTypeParameterResolver and CustomAnnotationParameterResolver, respectively.
public class ParameterResolverCustomTypeDemo {
@Test
@ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class })
void testInt(Integer i, WrappedInteger wrappedInteger) {
assertEquals(1, i);
assertEquals(2, wrappedInteger.value);
}
static class FirstIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(Integer.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 1;
}
}
static class SecondIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(WrappedInteger.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return new WrappedInteger(2);
}
}
static class WrappedInteger {
private final int value;
WrappedInteger(int value) {
this.value = value;
}
}
}
A custom annotation makes the duplicate type distinguishable from its counterpart:
public class ParameterResolverCustomAnnotationDemo {
@Test
void testInt(@FirstInteger Integer first, @SecondInteger Integer second) {
assertEquals(1, first);
assertEquals(2, second);
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(FirstInteger.Extension.class)
public @interface FirstInteger {
class Extension implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(Integer.class)
&& !parameterContext.isAnnotated(SecondInteger.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 1;
}
}
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SecondInteger.Extension.class)
public @interface SecondInteger {
class Extension implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.isAnnotated(SecondInteger.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return 2;
}
}
}
}
JUnit includes some built-in parameter resolvers that can cause conflicts if a resolver
attempts to claim their supported types. For example, TestInfo provides metadata about
tests. See Dependency Injection for Constructors and Methods for details. Third-party frameworks such
as Spring may also define parameter resolvers. Apply one of the techniques in this section
to resolve any conflicts.
Parameterized tests are another potential source of conflict. Ensure that tests annotated
with @ParameterizedTest are not also annotated with @Test and see
Consuming Arguments for more details.