From 8df778b039be6f165352be5372321dacba029c52 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Mon, 17 Mar 2025 18:35:03 +0100 Subject: [PATCH] GH-202 - Support for bean instances located in test sources in @ApplicationModuleTest. Prior to this commit, the TypeExcludeFilter registered by @ApplicationModuleTest decided whether to include a type based on the ApplicationModules instance and the content of the modules' backing JavaPackage instances. Those in turn always consider the classes scanned by ArchUnit to decide whether they include a type or not. As an ApplicationModules instance is set up to only consider production code, any type located in the test sources was disregarded from component scanning. The checks for package inclusion for a test execution have now been revamped to consider the sole package names when filtering types for inclusion. --- .../modulith/core/ApplicationModules.java | 7 +++++-- .../modulith/core/JavaPackage.java | 4 ++-- .../modulith/core/PackageName.java | 10 ++++++---- .../modulith/test/ModuleTestExecution.java | 15 +++++++++++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModules.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModules.java index ecfbe96e..9220b56e 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModules.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModules.java @@ -340,13 +340,16 @@ public class ApplicationModules implements Iterable { * modules. * * @param className must not be {@literal null} or empty. - * @return */ public boolean withinRootPackages(String className) { Assert.hasText(className, "Class name must not be null or empty!"); - return rootPackages.stream().anyMatch(it -> it.contains(className)); + var candidate = PackageName.ofType(className); + + return rootPackages.stream() + .map(JavaPackage::getPackageName) + .anyMatch(candidate::equals); } /** diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java index 5f19c989..b4cc818d 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java @@ -293,9 +293,9 @@ public class JavaPackage implements DescribedIterable, Comparable { +public class PackageName implements Comparable { private static final Map PACKAGE_NAMES = new HashMap<>(); @@ -68,8 +68,9 @@ class PackageName implements Comparable { * * @param fullyQualifiedName must not be {@literal null} or empty. * @return will never be {@literal null}. + * @since 1.4 */ - static PackageName ofType(String fullyQualifiedName) { + public static PackageName ofType(String fullyQualifiedName) { Assert.notNull(fullyQualifiedName, "Type name must not be null!"); @@ -191,8 +192,9 @@ class PackageName implements Comparable { * sub-package of it. * * @param reference must not be {@literal null}. + * @since 1.4 */ - boolean contains(PackageName reference) { + public boolean contains(PackageName reference) { Assert.notNull(reference, "Reference package name must not be null!"); diff --git a/spring-modulith-test/src/main/java/org/springframework/modulith/test/ModuleTestExecution.java b/spring-modulith-test/src/main/java/org/springframework/modulith/test/ModuleTestExecution.java index 828f7f9c..b71a1333 100644 --- a/spring-modulith-test/src/main/java/org/springframework/modulith/test/ModuleTestExecution.java +++ b/spring-modulith-test/src/main/java/org/springframework/modulith/test/ModuleTestExecution.java @@ -36,6 +36,7 @@ import org.springframework.modulith.core.ApplicationModule; import org.springframework.modulith.core.ApplicationModules; import org.springframework.modulith.core.ApplicationModulesFactory; import org.springframework.modulith.core.JavaPackage; +import org.springframework.modulith.core.PackageName; import org.springframework.modulith.test.ApplicationModuleTest.BootstrapMode; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -134,8 +135,7 @@ public class ModuleTestExecution implements Iterable { public boolean includes(String className) { - var result = modules.withinRootPackages(className) // - || basePackages.get().stream().anyMatch(it -> it.contains(className)); + var result = isLocatedInRootPackageOrContainedInBasePackages(className); if (result) { LOGGER.trace("Including class {}.", className); @@ -239,6 +239,17 @@ public class ModuleTestExecution implements Iterable { return Objects.hash(key); } + private boolean isLocatedInRootPackageOrContainedInBasePackages(String className) { + + if (modules.withinRootPackages(className)) { + return true; + } + + var candidate = PackageName.ofType(className); + + return basePackages.get().stream().map(JavaPackage::getPackageName).anyMatch(it -> it.contains(candidate)); + } + private static Stream getExtraModules(ApplicationModuleTest annotation, ApplicationModules modules) {