Skip to content

Commit 3495fc2

Browse files
authored
improve rule to not fail for classes with no tests in their packages (#1368)
This will improve the existing rule to test that test classes reside in the same package as their implementation. In particular the rule: Should not fail if there are multiple test classes with the same simple name and some of the classes have no tests at all Issue: #1367
2 parents 13a2bc9 + 2e7af6a commit 3495fc2

File tree

5 files changed

+36
-1
lines changed

5 files changed

+36
-1
lines changed

archunit/src/main/java/com/tngtech/archunit/library/GeneralCodingRules.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,12 +452,16 @@ public static ArchRule testClassesShouldResideInTheSamePackageAsImplementation(S
452452
private static ArchCondition<JavaClass> resideInTheSamePackageAsTheirTestClasses(String testClassSuffix) {
453453
return new ArchCondition<JavaClass>("reside in the same package as their test classes") {
454454
Map<String, List<JavaClass>> testClassesBySimpleClassName = new HashMap<>();
455+
Map<String, List<JavaClass>> classesByPackageName = new HashMap<>();
455456

456457
@Override
457458
public void init(Collection<JavaClass> allClasses) {
458459
testClassesBySimpleClassName = allClasses.stream()
459460
.filter(clazz -> clazz.getName().endsWith(testClassSuffix))
460461
.collect(groupingBy(JavaClass::getSimpleName));
462+
463+
classesByPackageName = allClasses.stream()
464+
.collect(groupingBy(JavaClass::getPackageName));
461465
}
462466

463467
@Override
@@ -468,7 +472,8 @@ public void check(JavaClass implementationClass, ConditionEvents events) {
468472
List<JavaClass> possibleTestClasses = testClassesBySimpleClassName.getOrDefault(possibleTestClassName, emptyList());
469473

470474
boolean isTestClassInWrongPackage = !possibleTestClasses.isEmpty()
471-
&& possibleTestClasses.stream().noneMatch(clazz -> clazz.getPackageName().equals(implementationClassPackageName));
475+
&& possibleTestClasses.stream().noneMatch(clazz -> clazz.getPackageName().equals(implementationClassPackageName))
476+
&& !allPossibleTestClassesHaveImplementationInRightPackage(possibleTestClasses);
472477

473478
if (isTestClassInWrongPackage) {
474479
possibleTestClasses.forEach(wrongTestClass -> {
@@ -478,6 +483,13 @@ public void check(JavaClass implementationClass, ConditionEvents events) {
478483
});
479484
}
480485
}
486+
487+
private boolean allPossibleTestClassesHaveImplementationInRightPackage(List<JavaClass> possibleTestClasses) {
488+
return possibleTestClasses.stream()
489+
.allMatch(testClazz -> classesByPackageName.getOrDefault(testClazz.getPackageName(), emptyList())
490+
.stream()
491+
.anyMatch(clazz -> testClazz.getSimpleName().equals(clazz.getSimpleName() + testClassSuffix)));
492+
}
481493
};
482494
}
483495

archunit/src/test/java/com/tngtech/archunit/library/GeneralCodingRulesTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.tngtech.archunit.library.testclasses.packages.correct.defaultsuffix.ImplementationClassWithCorrectPackage;
66
import com.tngtech.archunit.library.testclasses.packages.correct.notest.ImplementationClassWithoutTestClass;
77
import com.tngtech.archunit.library.testclasses.packages.correct.onedirmatching.ImplementationClassWithOneTestPackageMatchingOutOfTwo;
8+
import com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1.SimpleNameThatOccursInSeveralPackages;
89
import com.tngtech.archunit.library.testclasses.packages.incorrect.nodirmatching.ImplementationClassWithMultipleTestsNotMatchingImplementationClassPackage;
910
import com.tngtech.archunit.library.testclasses.packages.incorrect.wrongsubdir.customsuffix.ImplementationClassWithWrongTestClassPackageCustomSuffix;
1011
import com.tngtech.archunit.library.testclasses.packages.incorrect.wrongsubdir.customsuffix.subdir.ImplementationClassWithWrongTestClassPackageCustomSuffixTestingScenario;
@@ -89,6 +90,16 @@ public void should_not_pass_when_none_of_multiple_matching_test_classes_resides_
8990
);
9091
}
9192

93+
@Test
94+
public void should_pass_when_only_one_of_two_implementations_have_test_class_and_it_is_in_implementation_package() {
95+
assertThatRule(testClassesShouldResideInTheSamePackageAsImplementation())
96+
.checking(new ClassFileImporter().importPackagesOf(
97+
SimpleNameThatOccursInSeveralPackages.class,
98+
com.tngtech.archunit.library.testclasses.packages.incorrect.twoimplementationsonetestdir2.SimpleNameThatOccursInSeveralPackages.class
99+
))
100+
.hasNoViolation();
101+
}
102+
92103
@Test
93104
public void ASSERTIONS_SHOULD_HAVE_DETAIL_MESSAGE_should_fail_on_assert_without_detail_message() {
94105
@SuppressWarnings("unused")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1;
2+
3+
public class SimpleNameThatOccursInSeveralPackages {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1;
2+
3+
class SimpleNameThatOccursInSeveralPackagesTest {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.tngtech.archunit.library.testclasses.packages.incorrect.twoimplementationsonetestdir2;
2+
3+
public class SimpleNameThatOccursInSeveralPackages {
4+
}

0 commit comments

Comments
 (0)