junit-team/junit5 · error · PreconditionViolationException
Constructor injection is not supported for @ParameterizedCla
Error message
Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)
What it means
Thrown by ParameterizedClassExtension.createClassContext() when a @ParameterizedClass is combined with @TestInstance(Lifecycle.PER_CLASS) and constructor injection is detected. PER_CLASS creates a single test instance reused across all parameter sets, but constructor injection requires a new instance per parameter set — the two are fundamentally incompatible.
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 956246301e)
Solutions
- Switch to @TestInstance(Lifecycle.PER_METHOD) (the default) and remove @TestInstance(PER_CLASS) from the class
- If PER_CLASS is required, switch from constructor injection to field injection by annotating fields with @Parameter instead of using constructor parameters
- Move the parameterized parameters to a @ParameterizedTest method inside the class instead of using @ParameterizedClass
Example fix
// before
@ParameterizedClass
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@CsvSource("1, 2")
class MyTest {
MyTest(int a, int b) { } // constructor injection
}
// after (field injection with PER_CLASS)
@ParameterizedClass
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@CsvSource("1, 2")
class MyTest {
@Parameter(0) int a;
@Parameter(1) int b;
} Defensive patterns
Strategy: validation
Validate before calling
// Before combining @ParameterizedClass with PER_CLASS, check injection type:
boolean isPerClass = lifecycle == TestInstance.Lifecycle.PER_CLASS;
boolean usesConstructorInjection = Arrays.stream(testClass.getDeclaredConstructors())
.anyMatch(c -> c.getParameterCount() > 0);
if (isPerClass && usesConstructorInjection) {
throw new IllegalStateException(
"Cannot use @ParameterizedClass with PER_CLASS and constructor injection");
} Prevention
- Do not combine @TestInstance(Lifecycle.PER_CLASS) with @ParameterizedClass using constructor parameters
- If PER_CLASS is needed, use @Parameter field injection instead of constructor injection
- Default to PER_METHOD lifecycle (omit @TestInstance) for parameterized classes with constructor injection
When it happens
Trigger: Annotating a class with both @ParameterizedClass and @TestInstance(TestInstance.Lifecycle.PER_CLASS) where the class constructor declares parameters (i.e., the injection type resolved to CONSTRUCTOR). ParameterizedClassContext determines the injection type by inspecting whether the constructor has parameters.
Common situations: Migrating a @ParameterizedTest class to @ParameterizedClass while keeping @TestInstance(PER_CLASS) (often used to share state across test methods or for @BeforeAll/@AfterAll non-static methods). Copying a PER_CLASS pattern from a regular test class to a parameterized class.
Related errors
- Configuration error: <error>.
- %d configuration errors:%n%s
- Configuration error: You must configure at least one set of
- The display name pattern defined for the parameterized test
- When the display name pattern for a @%s contains %s, the arg
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/4511c82b50a13304.json.
Report an issue: GitHub.