Skip to content

Commit

Permalink
improve rule to not fail for classes with no tests in their packages (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
hankem authored Feb 10, 2025
2 parents 13a2bc9 + 2e7af6a commit 3495fc2
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -452,12 +452,16 @@ public static ArchRule testClassesShouldResideInTheSamePackageAsImplementation(S
private static ArchCondition<JavaClass> resideInTheSamePackageAsTheirTestClasses(String testClassSuffix) {
return new ArchCondition<JavaClass>("reside in the same package as their test classes") {
Map<String, List<JavaClass>> testClassesBySimpleClassName = new HashMap<>();
Map<String, List<JavaClass>> classesByPackageName = new HashMap<>();

@Override
public void init(Collection<JavaClass> allClasses) {
testClassesBySimpleClassName = allClasses.stream()
.filter(clazz -> clazz.getName().endsWith(testClassSuffix))
.collect(groupingBy(JavaClass::getSimpleName));

classesByPackageName = allClasses.stream()
.collect(groupingBy(JavaClass::getPackageName));
}

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

boolean isTestClassInWrongPackage = !possibleTestClasses.isEmpty()
&& possibleTestClasses.stream().noneMatch(clazz -> clazz.getPackageName().equals(implementationClassPackageName));
&& possibleTestClasses.stream().noneMatch(clazz -> clazz.getPackageName().equals(implementationClassPackageName))
&& !allPossibleTestClassesHaveImplementationInRightPackage(possibleTestClasses);

if (isTestClassInWrongPackage) {
possibleTestClasses.forEach(wrongTestClass -> {
Expand All @@ -478,6 +483,13 @@ public void check(JavaClass implementationClass, ConditionEvents events) {
});
}
}

private boolean allPossibleTestClassesHaveImplementationInRightPackage(List<JavaClass> possibleTestClasses) {
return possibleTestClasses.stream()
.allMatch(testClazz -> classesByPackageName.getOrDefault(testClazz.getPackageName(), emptyList())
.stream()
.anyMatch(clazz -> testClazz.getSimpleName().equals(clazz.getSimpleName() + testClassSuffix)));
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.tngtech.archunit.library.testclasses.packages.correct.defaultsuffix.ImplementationClassWithCorrectPackage;
import com.tngtech.archunit.library.testclasses.packages.correct.notest.ImplementationClassWithoutTestClass;
import com.tngtech.archunit.library.testclasses.packages.correct.onedirmatching.ImplementationClassWithOneTestPackageMatchingOutOfTwo;
import com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1.SimpleNameThatOccursInSeveralPackages;
import com.tngtech.archunit.library.testclasses.packages.incorrect.nodirmatching.ImplementationClassWithMultipleTestsNotMatchingImplementationClassPackage;
import com.tngtech.archunit.library.testclasses.packages.incorrect.wrongsubdir.customsuffix.ImplementationClassWithWrongTestClassPackageCustomSuffix;
import com.tngtech.archunit.library.testclasses.packages.incorrect.wrongsubdir.customsuffix.subdir.ImplementationClassWithWrongTestClassPackageCustomSuffixTestingScenario;
Expand Down Expand Up @@ -89,6 +90,16 @@ public void should_not_pass_when_none_of_multiple_matching_test_classes_resides_
);
}

@Test
public void should_pass_when_only_one_of_two_implementations_have_test_class_and_it_is_in_implementation_package() {
assertThatRule(testClassesShouldResideInTheSamePackageAsImplementation())
.checking(new ClassFileImporter().importPackagesOf(
SimpleNameThatOccursInSeveralPackages.class,
com.tngtech.archunit.library.testclasses.packages.incorrect.twoimplementationsonetestdir2.SimpleNameThatOccursInSeveralPackages.class
))
.hasNoViolation();
}

@Test
public void ASSERTIONS_SHOULD_HAVE_DETAIL_MESSAGE_should_fail_on_assert_without_detail_message() {
@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1;

public class SimpleNameThatOccursInSeveralPackages {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tngtech.archunit.library.testclasses.packages.correct.twoimplementationsonetestdir1;

class SimpleNameThatOccursInSeveralPackagesTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tngtech.archunit.library.testclasses.packages.incorrect.twoimplementationsonetestdir2;

public class SimpleNameThatOccursInSeveralPackages {
}

0 comments on commit 3495fc2

Please sign in to comment.