or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/maven-com-tngtech-archunit--archunit-junit5-engine

JUnit 5 test engine for ArchUnit that enables running architecture tests as part of JUnit 5 test suites

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.tngtech.archunit/archunit-junit5-engine@1.4.x

To install, run

npx @tessl/cli install tessl/maven-com-tngtech-archunit--archunit-junit5-engine@1.4.0

index.mddocs/

ArchUnit JUnit 5 Engine

ArchUnit JUnit 5 Engine provides seamless integration between ArchUnit architecture testing and JUnit 5 test suites. This engine enables automatic discovery and execution of ArchUnit architecture tests as part of the JUnit Platform, allowing architectural constraints to be validated during regular test execution with full IDE and build tool support.

Package Information

  • Package Name: com.tngtech.archunit:archunit-junit5-engine
  • Package Type: Maven
  • Language: Java
  • Installation: Add to test dependencies in build file

Gradle

testImplementation 'com.tngtech.archunit:archunit-junit5-engine:1.4.1'

Maven

<dependency>
    <groupId>com.tngtech.archunit</groupId>
    <artifactId>archunit-junit5-engine</artifactId>
    <version>1.4.1</version>
    <scope>test</scope>
</dependency>

Core Imports

import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchIgnore;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTests;
import com.tngtech.archunit.junit.CacheMode;
import com.tngtech.archunit.junit.LocationProvider;
import com.tngtech.archunit.junit.engine_api.FieldSelector;
import com.tngtech.archunit.junit.engine_api.FieldSource;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.core.importer.Location;
import java.util.Set;
import java.lang.reflect.Field;

Basic Usage

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;

@AnalyzeClasses(packages = "com.myapp")
public class ArchitectureTest {
    
    @ArchTest
    public static final ArchRule layered_architecture = 
        layeredArchitecture()
            .layer("Controller").definedBy("..controller..")
            .layer("Service").definedBy("..service..")
            .layer("Repository").definedBy("..repository..")
            .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
            .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
            .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service");
    
    @ArchTest
    public void services_should_only_be_accessed_by_controllers(JavaClasses classes) {
        classes().that().resideInAPackage("..service..")
            .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..")
            .check(classes);
    }
}

Architecture

The ArchUnit JUnit 5 Engine is built around several key components:

  • Test Engine: Core JUnit Platform integration via ArchUnitTestEngine registered as 'archunit' engine
  • Test Discovery: Automatic scanning for @AnalyzeClasses annotated classes containing @ArchTest fields/methods
  • Test Execution: Executes ArchUnit rules against imported Java classes with comprehensive error reporting
  • Descriptor Hierarchy: Organizes tests into hierarchical structure (Engine → Class → Field/Method descriptors)
  • Caching System: Optimizes performance by caching imported Java classes across test executions
  • Filtering Support: Supports JUnit Platform test filtering and custom ArchUnit property-based filtering

Capabilities

Test Discovery and Selectors

Provides JUnit Platform discovery selectors for programmatic test selection beyond standard class/method selection.

/**
 * Selector for ArchUnit test fields, enables programmatic discovery of specific @ArchTest fields
 */
public final class FieldSelector implements DiscoverySelector {
    /**
     * Select a field by class name and field name
     * @param className - Fully qualified class name containing the field
     * @param fieldName - Name of the @ArchTest field to select
     * @return FieldSelector for the specified field
     */
    public static FieldSelector selectField(String className, String fieldName);
    
    /**
     * Select a field by class and field name
     * @param javaClass - Class containing the field
     * @param fieldName - Name of the @ArchTest field to select  
     * @return FieldSelector for the specified field
     */
    public static FieldSelector selectField(Class<?> javaClass, String fieldName);
    
    /**
     * Select a field by class and field instance
     * @param javaClass - Class containing the field
     * @param field - The @ArchTest field to select
     * @return FieldSelector for the specified field
     */
    public static FieldSelector selectField(Class<?> javaClass, Field field);
    
    /** Get the Java class containing the selected field */
    @Internal
    public Class<?> getJavaClass();
    
    /** Get the selected Java field */
    @Internal
    public Field getJavaField();
}

Test Source Information

Provides detailed source information for ArchUnit test fields enabling IDE navigation and reporting.

/**
 * Test source implementation for ArchUnit test fields
 */
@PublicAPI(usage = ACCESS)
public final class FieldSource implements TestSource {
    /**
     * Create FieldSource from Field instance - internal use only
     * @param field - The field to create source for
     * @return FieldSource instance
     */
    @Internal
    static FieldSource from(Field field);
    
    /** Get the fully qualified class name containing the field */
    @PublicAPI(usage = ACCESS)
    public String getClassName();
    
    /** Get the Java class containing the field */
    @PublicAPI(usage = ACCESS)
    public Class<?> getJavaClass();
    
    /** Get the field name */
    @PublicAPI(usage = ACCESS)
    public String getFieldName();
}

Test Engine Registration

The engine automatically registers with JUnit Platform through service provider interface.

Service Registration:

  • File: META-INF/services/org.junit.platform.engine.TestEngine
  • Implementation: com.tngtech.archunit.junit.internal.ArchUnitTestEngine
  • Engine ID: "archunit"

Required Annotations

Tests must use these annotations from the core ArchUnit library:

/**
 * Specifies which packages/locations should be scanned when running JUnit 5 tests
 */
@Testable
@Target({TYPE})
@Retention(RUNTIME)
@PublicAPI(usage = ACCESS)
public @interface AnalyzeClasses {
    /** Packages to look for within the classpath/modulepath */
    String[] packages() default {};
    
    /** Classes that specify packages to look for */
    Class<?>[] packagesOf() default {};
    
    /** Implementations of LocationProvider for custom class sources */
    Class<? extends LocationProvider>[] locations() default {};
    
    /** Whether to look for classes on the whole classpath */
    boolean wholeClasspath() default false;
    
    /** Types of ImportOption to use for filtering the class import */
    Class<? extends ImportOption>[] importOptions() default {};
    
    /** Controls if JavaClasses should be cached by location */
    CacheMode cacheMode() default CacheMode.FOREVER;
}

/**
 * Marks ArchUnit tests to be executed by the test infrastructure
 */
@Testable
@Target({FIELD, METHOD})
@Retention(RUNTIME)
public @interface ArchTest {
}

/**
 * Marks rules to be ignored by the test support
 */
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface ArchIgnore {
    /** Why the test is ignored */
    String reason() default "";
}

/**
 * Repeatable annotation for tagging ArchTest fields/methods/classes
 */
@Inherited
@Documented
@Retention(RUNTIME)
@PublicAPI(usage = ACCESS)
@Repeatable(ArchTags.class)
@Target({TYPE, METHOD, FIELD})
public @interface ArchTag {
    /** The actual tag value - must adhere to JUnit Platform tag syntax rules */
    String value();
}

Integration with ArchUnit Core

The engine integrates with core ArchUnit types and functionality:

/**
 * Collections of ArchUnit test rules for inclusion in test classes
 */
@PublicAPI(usage = ACCESS)
public final class ArchTests {
    /**
     * @param definitionLocation The class whose @ArchTest members should be included in this test
     * @return the ArchTests of the supplied class
     */
    @PublicAPI(usage = ACCESS)
    public static ArchTests in(Class<?> definitionLocation);
}

/**
 * Allows custom implementation to supply Locations to be imported by the JUnit test infrastructure
 */
@PublicAPI(usage = INHERITANCE)
public interface LocationProvider {
    /**
     * Returns locations to be imported for the current test run
     * @param testClass The class object of the test currently executed
     * @return The locations to import
     */
    Set<Location> get(Class<?> testClass);
}

/**
 * Determines how the JUnit test support caches classes
 */
@PublicAPI(usage = ACCESS)
public enum CacheMode {
    /** Cache imported Java classes for current test class only */
    @PublicAPI(usage = ACCESS)
    PER_CLASS,
    
    /** Cache imported Java classes by location using SoftReferences */
    @PublicAPI(usage = ACCESS)
    FOREVER
}

Test Execution Patterns

Field-based Tests (Recommended):

@AnalyzeClasses(packages = "com.example")
public class MyArchTest {
    @ArchTest
    public static final ArchRule rule = classes()
        .that().resideInAPackage("..service..")
        .should().notDependOn("..controller..");
}

Method-based Tests:

@AnalyzeClasses(packages = "com.example")
public class MyArchTest {
    @ArchTest
    public void methodTest(JavaClasses classes) {
        classes().that().resideInAPackage("..service..")
            .should().notDependOn("..controller..")
            .check(classes);
    }
}

Rule Libraries:

@AnalyzeClasses(packages = "com.example")
public class MyArchTest {
    @ArchTest
    public static final ArchTests rules = ArchTests.in(MyRuleLibrary.class);
}

Test Filtering

Property-based Filtering:

# In archunit.properties or system property
junit.testFilter=specificRuleName,anotherRule

JUnit Platform Filtering: Standard JUnit 5 filters work: class name filters, package filters, tags, etc.

Error Handling

The engine provides comprehensive error reporting:

  • Initialization Errors: Clear messages for missing @AnalyzeClasses annotations or invalid test method signatures
  • Rule Violations: Detailed violation reports with class/method/field references and suggested fixes
  • Class Loading Issues: Graceful handling of missing classes with informative error messages
  • Configuration Errors: Validation of test method parameters (must accept single JavaClasses parameter)

Performance Considerations

  • Class Cache: Automatically caches imported JavaClasses to avoid repeated expensive import operations
  • Lazy Evaluation: Test discovery and class analysis occur only when needed
  • Memory Management: Cache is cleared per test class to prevent memory leaks
  • Concurrent Execution: Thread-safe design supports parallel test execution

Types

// Key internal types (for understanding engine behavior)
interface CreatesChildren {
    void createChildren(ElementResolver resolver);
}

// Test descriptor types extend JUnit Platform interfaces
abstract class AbstractArchUnitTestDescriptor extends AbstractTestDescriptor 
    implements Node<ArchUnitEngineExecutionContext>

class ArchUnitTestEngine extends HierarchicalTestEngine<ArchUnitEngineExecutionContext>
class ArchUnitEngineDescriptor extends EngineDescriptor 
    implements Node<ArchUnitEngineExecutionContext>