This version is still in development and is not considered stable yet.
For the latest stable version, please use JUnit 6.0.1!

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.

Due to a bug in the byte code generated by javac on JDK versions prior to JDK 9, looking up annotations on parameters directly via the core java.lang.reflect.Parameter API will always fail for inner class constructors (e.g., a constructor in a @Nested test class).

The ParameterContext API supplied to ParameterResolver implementations therefore includes the following convenience methods for correctly looking up annotations on parameters. Extension authors are strongly encouraged to use these methods instead of those provided in java.lang.reflect.Parameter in order to avoid this bug in the JDK.

  • boolean isAnnotated(Class<? extends Annotation> annotationType)

  • Optional<A> findAnnotation(Class<A> annotationType)

  • List<A> findRepeatableAnnotations(Class<A> annotationType)

Accessing the test-scoped ExtensionContext

You may override the getTestInstantiationExtensionContextScope(…​) method to return TEST_METHOD to support injecting test specific data into constructor parameters of the test class instance. Doing so causes a test-specific ExtensionContext to be used while resolving constructor parameters, unless the test instance lifecycle is set to PER_CLASS.

Parameter resolution for methods called from extensions

Other extensions can also leverage registered ParameterResolvers for method and constructor invocations, using the ExecutableInvoker available via the getExecutableInvoker() method in the ExtensionContext.

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:

Conflicting parameter resolution due to multiple resolvers claiming support for integers
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.

Fine-grained registration to avoid conflict
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.

Custom type to resolve duplicate types
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;

		public WrappedInteger(int value) {
			this.value = value;
		}

	}
}

A custom annotation makes the duplicate type distinguishable from its counterpart:

Custom annotation to resolve duplicate types
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.