junit-team/junit5 · error · PreconditionViolationException
Constructor injection is not supported for…
Error message
Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)
What it means
Thrown when a @ParameterizedClass is configured with constructor-based argument injection (i.e., the @ParameterizedClass constructor takes parameters that are supplied by the arguments source) AND the class is annotated with @TestInstance(Lifecycle.PER_CLASS). PER_CLASS means a single test instance is reused across all invocations, but constructor injection with parameterized arguments requires creating a new instance per argument set — these are fundamentally incompatible.
Solutions
- Switch to @TestInstance(Lifecycle.PER_METHOD) (the default) which creates a new instance per invocation and supports constructor injection
- Keep PER_CLASS but switch from constructor injection to field injection using @Parameter-annotated fields
- Keep PER_CLASS and constructor injection but remove the parameterized constructor arguments — use @Parameter fields or method parameters instead
Example fix
// before — incompatible combination
@ParameterizedClass
@TestInstance(Lifecycle.PER_CLASS)
class MyTest {
private final int value;
MyTest(@Parameter int value) { this.value = value; }
}
// after — use field injection with PER_CLASS
@ParameterizedClass
@TestInstance(Lifecycle.PER_CLASS)
class MyTest {
@Parameter int value;
}
// OR — use constructor injection with PER_METHOD (default)
@ParameterizedClass
class MyTest {
private final int value;
MyTest(@Parameter int value) { this.value = value; }
} Defensive patterns
Strategy: validation
Validate before calling
// Before annotating a @ParameterizedClass, check for the PER_CLASS + constructor injection conflict
import org.junit.jupiter.api.TestInstance;
boolean isPerClass = testClass.isAnnotationPresent(TestInstance.class)
&& testClass.getAnnotation(TestInstance.class).value() == TestInstance.Lifecycle.PER_CLASS;
boolean hasParameterizedConstructor = java.util.Arrays.stream(testClass.getDeclaredConstructors())
.anyMatch(c -> c.getParameterCount() > 0);
if (isPerClass && hasParameterizedConstructor && testClass.isAnnotationPresent(ParameterizedClass.class)) {
throw new IllegalStateException(
"Constructor injection is not supported with @TestInstance(PER_CLASS). Use field injection (@Parameter) or switch to PER_METHOD.");
} Prevention
- Default to @TestInstance(PER_METHOD) for @ParameterizedClass unless you specifically need shared state
- If using PER_CLASS, prefer @Parameter-annotated fields over constructor parameters for argument injection
- Document the PER_CLASS + constructor injection incompatibility in your project's testing guidelines
When it happens
Trigger: Annotate a class with both @ParameterizedClass and @TestInstance(Lifecycle.PER_CLASS), and have the constructor accept @Parameter-annotated or positional arguments from the source. The createClassContext method detects injectionType == CONSTRUCTOR combined with lifecycle == PER_CLASS and throws.
Common situations: Developers migrating from @ParameterizedTest (method-level) to @ParameterizedClass (class-level) who carry over the PER_CLASS lifecycle. Or developers who want to share state across parameterized invocations (hence PER_CLASS) but also want constructor injection for clean parameter binding — these two desires conflict.
Related errors
- Configuration error: .
- Configuration error: You must configure at least one set of…
- configuration errors:%n
- Failed to inject parameter value into field
- The display name pattern defined for the parameterized test…
AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11).
Data as JSON: /api/errors/4511c82b50a13304.
Report an issue: GitHub.
Appendix: source
Thrown at junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassExtension.java:119
return false;
}
store.put(DECLARATION_CONTEXT_KEY,
createClassContext(extensionContext, extensionContext.getRequiredTestClass(), annotation.get()));
return true;
}
private static ParameterizedClassContext createClassContext(ExtensionContext extensionContext, Class<?> testClass,
ParameterizedClass annotation) {
TestInstance.Lifecycle lifecycle = extensionContext.getTestInstanceLifecycle() //
.orElseThrow(() -> new PreconditionViolationException("TestInstance.Lifecycle not present"));
ParameterizedClassContext classContext = new ParameterizedClassContext(testClass, annotation, lifecycle);
if (lifecycle == PER_CLASS && classContext.getInjectionType() == CONSTRUCTOR) {
throw new PreconditionViolationException(
"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)");
}
return classContext;
}
private ParameterizedClassContext getDeclarationContext(ExtensionContext extensionContext) {
return requireNonNull(getStore(extensionContext)//
.get(DECLARATION_CONTEXT_KEY, ParameterizedClassContext.class));
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(ParameterizedClassExtension.class, context.getRequiredTestClass()));
}
}
View on GitHub (pinned to f070c699a0)