From a601076510d221e7c03bb765b9a22c86b1be563b Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 10 Apr 2026 21:45:41 +0000 Subject: [PATCH] fix: detect JUnit 5 from submodule build files and default to JUnit 5 Multi-module Gradle projects declare JUnit 5 dependencies in submodule build files, not the root. The detection function only checked the root build.gradle, missing JUnit 5 entirely and falling back to JUnit 4. Now scans immediate child directories for build files. Also changes the default framework from JUnit 4 to JUnit 5 (standard since 2017). Co-Authored-By: Claude Opus 4.6 --- codeflash/languages/java/config.py | 23 +++++++-- tests/test_languages/test_java/test_config.py | 49 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/codeflash/languages/java/config.py b/codeflash/languages/java/config.py index 788c93c50..e3f9426a9 100644 --- a/codeflash/languages/java/config.py +++ b/codeflash/languages/java/config.py @@ -163,9 +163,9 @@ def _detect_test_framework(project_root: Path, build_tool: BuildTool) -> tuple[s logger.debug("Selected TestNG as test framework") return "testng", has_junit5, has_junit4, has_testng - # Default to JUnit 4 if nothing detected (more common in legacy projects) - logger.debug("No test framework detected, defaulting to JUnit 4") - return "junit4", has_junit5, has_junit4, has_testng + # Default to JUnit 5 if nothing detected (standard since 2017) + logger.debug("No test framework detected, defaulting to JUnit 5") + return "junit5", has_junit5, has_junit4, has_testng def _detect_test_deps_from_pom(project_root: Path) -> tuple[bool, bool, bool]: @@ -286,6 +286,23 @@ def _detect_test_deps_from_gradle(project_root: Path) -> tuple[bool, bool, bool] except Exception: pass + # For multi-module projects, check submodule build files if root has no test deps + if not (has_junit5 or has_junit4 or has_testng): + for sub_gradle in sorted(project_root.glob("*/build.gradle*")): + if sub_gradle.name in ("build.gradle", "build.gradle.kts"): + try: + content = sub_gradle.read_text(encoding="utf-8") + if "junit-jupiter" in content or "useJUnitPlatform" in content: + has_junit5 = True + if "junit:junit" in content: + has_junit4 = True + if "testng" in content.lower(): + has_testng = True + if has_junit5: + break # JUnit 5 found, no need to check more + except Exception: + pass + return has_junit5, has_junit4, has_testng diff --git a/tests/test_languages/test_java/test_config.py b/tests/test_languages/test_java/test_config.py index 1f8397e50..2b64f0558 100644 --- a/tests/test_languages/test_java/test_config.py +++ b/tests/test_languages/test_java/test_config.py @@ -7,6 +7,8 @@ from codeflash.languages.java.build_tools import BuildTool from codeflash.languages.java.config import ( JavaProjectConfig, + _detect_test_deps_from_gradle, + _detect_test_framework, detect_java_project, get_test_class_pattern, get_test_file_pattern, @@ -342,3 +344,50 @@ def test_detect_fixture_project(self, java_fixture_path: Path): assert config.source_root is not None assert config.test_root is not None assert config.has_junit5 is True + + +class TestGradleSubmoduleDetection: + + def test_detect_gradle_junit5_from_submodule(self, tmp_path: Path) -> None: + root = tmp_path.resolve() + # Root build.gradle with no test deps + (root / "build.gradle.kts").write_text("plugins { java }\n", encoding="utf-8") + (root / "settings.gradle.kts").write_text('include("submodule-a")\n', encoding="utf-8") + + # Submodule with JUnit 5 + sub = root / "submodule-a" + sub.mkdir() + (sub / "build.gradle.kts").write_text( + 'dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") }\n' + "tasks.withType { useJUnitPlatform() }\n", + encoding="utf-8", + ) + + has_junit5, has_junit4, has_testng = _detect_test_deps_from_gradle(root) + assert has_junit5 is True + assert has_junit4 is False + assert has_testng is False + + def test_detect_gradle_junit5_from_root(self, tmp_path: Path) -> None: + root = tmp_path.resolve() + (root / "build.gradle").write_text( + "dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' }\n" + "test { useJUnitPlatform() }\n", + encoding="utf-8", + ) + + has_junit5, has_junit4, has_testng = _detect_test_deps_from_gradle(root) + assert has_junit5 is True + assert has_junit4 is False + assert has_testng is False + + def test_default_to_junit5_when_nothing_detected(self, tmp_path: Path) -> None: + root = tmp_path.resolve() + # Empty Gradle project — no test deps anywhere + (root / "build.gradle.kts").write_text("plugins { java }\n", encoding="utf-8") + + framework, has_junit5, has_junit4, has_testng = _detect_test_framework(root, BuildTool.GRADLE) + assert framework == "junit5" + assert has_junit5 is False + assert has_junit4 is False + assert has_testng is False