{"record":{"id":"6b1e8238535b8f86","repo":"junit-team/junit5","slug":"testfactory-method-must-not-return-null","errorCode":null,"errorMessage":"@TestFactory method must not return null","messagePattern":"@TestFactory method must not return null","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java","lineNumber":139,"sourceCode":"\t\t\t\twhile (iterator.hasNext()) {\n\t\t\t\t\tDynamicNode dynamicNode = iterator.next();\n\t\t\t\t\tOptional<JupiterTestDescriptor> descriptor = createDynamicDescriptor(this, dynamicNode, index,\n\t\t\t\t\t\tdefaultTestSource, getDynamicDescendantFilter(), configuration);\n\t\t\t\t\tdescriptor.ifPresent(dynamicTestExecutor::execute);\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ClassCastException ex) {\n\t\t\t\tthrow invalidReturnTypeException(ex);\n\t\t\t}\n\t\t\tdynamicTestExecutor.awaitFinished();\n\t\t});\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Stream<DynamicNode> toDynamicNodeStream(@Nullable Object testFactoryMethodResult) {\n\t\tif (testFactoryMethodResult == null) {\n\t\t\tthrow new JUnitException(\"@TestFactory method must not return null\");\n\t\t}\n\t\tif (testFactoryMethodResult instanceof DynamicNode node) {\n\t\t\treturn Stream.of(node);\n\t\t}\n\t\treturn (Stream<DynamicNode>) CollectionUtils.toStream(testFactoryMethodResult);\n\t}\n\n\tprivate JUnitException invalidReturnTypeException(Throwable cause) {\n\t\tString message = \"Objects produced by @TestFactory method '%s' must be of type %s.\".formatted(\n\t\t\tgetTestMethod().toGenericString(), DynamicNode.class.getName());\n\t\treturn new JUnitException(message, cause);\n\t}\n\n\tstatic Optional<JupiterTestDescriptor> createDynamicDescriptor(JupiterTestDescriptor parent, DynamicNode node,\n\t\t\tint index, TestSource defaultTestSource, DynamicDescendantFilter dynamicDescendantFilter,\n\t\t\tJupiterConfiguration configuration) {\n\n\t\tUniqueId uniqueId;","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java#L121-L157","documentation":"Thrown when a @TestFactory method returns null. JUnit Jupiter treats @TestFactory methods as dynamic-test producers; null is neither a single DynamicNode nor a convertible collection/stream, so there is no way to enumerate tests and the framework aborts with an explicit message instead of silently producing zero tests.","triggerScenarios":"toDynamicNodeStream(@Nullable Object) is called with a null result from the @TestFactory method; the explicit null check at the top of that method throws JUnitException.","commonSituations":"Method returns null by accident (early return, missing return statement, conditional return that falls through), or returns null to signal 'no tests' which is not allowed - use Collections.emptyList() or Stream.empty() instead.","solutions":["Return Stream.empty(), Collections.emptyList(), or an empty Iterable instead of null to express 'no dynamic tests'.","Fix the missing return statement or the conditional that fell through to null.","Use a single DynamicTest.dynamicTest(...) when you have exactly one test."],"exampleFix":"// before\n@TestFactory\nStream<DynamicTest> factory() {\n    if (inputs.isEmpty()) return null;\n    return inputs.stream().map(i -> dynamicTest(i, () -> run(i)));\n}\n// after\n@TestFactory\nStream<DynamicTest> factory() {\n    if (inputs.isEmpty()) return Stream.empty();\n    return inputs.stream().map(i -> dynamicTest(i, () -> run(i)));\n}","handlingStrategy":"validation","validationCode":"Object result = invokeTestFactory();\nif (result == null) {\n    throw new IllegalStateException(\"@TestFactory returned null - return Stream.empty() instead\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always return a non-null Stream/Iterable/DynamicNode from @TestFactory methods.","Use Stream.empty() or Collections.emptyList() to express 'no tests'.","Add a unit test that asserts the factory method's return value is non-null."],"tags":["test-factory","dynamic-test","null-return","api-misuse"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}