5

I'm trying to make a unit test with ArchUnit to check if I have any unused classes. But I can't figure out how to check if some class is referenced with MyClass.class.

For example I have a class:

public class MyClass {
    ...
}

Then I reference this class in some method:

public class MySecondClass{
    public void methodA(){
        methodThatTakesClassAsParameter(MyClass.class);
    }
    ...
}

How can I see from ArchUnit that MyClass is referenced from MySecondClass?

Ralph
  • 118,862
  • 56
  • 287
  • 383
Mika K
  • 101
  • 4
  • I think that is discussed as an issue of the project on github https://github.com/TNG/ArchUnit/issues/131 and doesn't have a solution yet. – Simulant Aug 08 '19 at 12:37

1 Answers1

2

I might have a (partial) solution for you.

Create an ArchCondition

  public static class NotBeUnreferenced extends ArchCondition<JavaClass> {

      NotBeUnreferenced() {
          super("is not referenced by any other compilation units");
      }

      @Override
      public void check(JavaClass javaClass, ConditionEvents events) {
          Set<JavaAccess<?>> accessesFromOtherCompilationUnits = new HashSet<JavaAccess<?>>();
          accessesFromOtherCompilationUnits.addAll(javaClass.getAccessesToSelf());
          accessesFromOtherCompilationUnits.removeAll(javaClass.getAccessesFromSelf());

          if (accessesFromOtherCompilationUnits.isEmpty() && javaClass.getDirectDependenciesToSelf().isEmpty()) {
              String message = createCheckMessage(javaClass, "is unreferenced");
              events.add(new SimpleConditionEvent(javaClass, false, message));
          }
      }

      // taken from com.tngtech.archunit.lang.conditions.ArchConditions
      public static <T extends HasDescription & HasSourceCodeLocation> String createCheckMessage(T object,
              String message) {
          return object.getDescription() + " " + message + " in " + object.getSourceCodeLocation();
      }
  }

Use the ArchCondition

  ArchCondition<JavaClass> notBeUnreferenced = new NotBeUnreferenced();
  ArchRule rule = classes().should(notBeUnreferenced);
  collectedRules.add(rule);

You might want to

  • exclude classes with certain annotations (e.g., @EJB, @Dependent, @Stateless) by appending an or-condition (rule = rule.or(ArchConditions.beAnnotatedWith(...)))
  • exclude code from test classes
  • exclude classes and interfaces that only exhibit constants

Known limitations

Note that a compilation unit (class, interface, enum) is considered unreferenced if the only access is

  • an access to .class and other reflective access
  • an access to a constant (i.e., a static final field)
nrainer
  • 2,542
  • 2
  • 23
  • 35
  • 2
    Many thanks @nrainer ; I've expanded on your sample here to detect unused code in my projects: https://github.com/timtebeek/archunit-unreferenced – Tim Jan 03 '21 at 17:35