{"id":"4511c82b50a13304","repo":"junit-team/junit5","slug":"constructor-injection-is-not-supported-for-parame","errorCode":null,"errorMessage":"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)","messagePattern":"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance\\(Lifecycle\\.PER_CLASS\\)","errorType":"exception","errorClass":"PreconditionViolationException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassExtension.java","lineNumber":119,"sourceCode":"\t\t\treturn false;\n\t\t}\n\n\t\tstore.put(DECLARATION_CONTEXT_KEY,\n\t\t\tcreateClassContext(extensionContext, extensionContext.getRequiredTestClass(), annotation.get()));\n\n\t\treturn true;\n\t}\n\n\tprivate static ParameterizedClassContext createClassContext(ExtensionContext extensionContext, Class<?> testClass,\n\t\t\tParameterizedClass annotation) {\n\n\t\tTestInstance.Lifecycle lifecycle = extensionContext.getTestInstanceLifecycle() //\n\t\t\t\t.orElseThrow(() -> new PreconditionViolationException(\"TestInstance.Lifecycle not present\"));\n\n\t\tParameterizedClassContext classContext = new ParameterizedClassContext(testClass, annotation, lifecycle);\n\n\t\tif (lifecycle == PER_CLASS && classContext.getInjectionType() == CONSTRUCTOR) {\n\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)\");\n\t\t}\n\n\t\treturn classContext;\n\t}\n\n\tprivate ParameterizedClassContext getDeclarationContext(ExtensionContext extensionContext) {\n\t\treturn requireNonNull(getStore(extensionContext)//\n\t\t\t\t.get(DECLARATION_CONTEXT_KEY, ParameterizedClassContext.class));\n\t}\n\n\tprivate Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(Namespace.create(ParameterizedClassExtension.class, context.getRequiredTestClass()));\n\t}\n\n}\n","sourceCodeStart":101,"sourceCodeEnd":136,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassExtension.java#L101-L136","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n@ParameterizedClass\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n@CsvSource(\"1, 2\")\nclass MyTest {\n    MyTest(int a, int b) { } // constructor injection\n}\n\n// after (field injection with PER_CLASS)\n@ParameterizedClass\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n@CsvSource(\"1, 2\")\nclass MyTest {\n    @Parameter(0) int a;\n    @Parameter(1) int b;\n}","handlingStrategy":"validation","validationCode":"// Before combining @ParameterizedClass with PER_CLASS, check injection type:\nboolean isPerClass = lifecycle == TestInstance.Lifecycle.PER_CLASS;\nboolean usesConstructorInjection = Arrays.stream(testClass.getDeclaredConstructors())\n    .anyMatch(c -> c.getParameterCount() > 0);\nif (isPerClass && usesConstructorInjection) {\n    throw new IllegalStateException(\n        \"Cannot use @ParameterizedClass with PER_CLASS and constructor injection\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["parameterized-class","test-instance","constructor-injection","configuration","junit-jupiter-params"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}