Recipe: AtlasArc.io CI with JUnit

Put the complete configured AtlasArc evaluator in an ordinary JUnit 5 test. The adapter runs in-process, reads the same .atlasarc/evaluator.json as the standalone CLI, and turns an unaccepted cycle or invalid input into a normal assertion failure. You do not need ArchUnit annotations or a separate evaluator process.

For JVM sources, the configured evaluator still uses ArchUnit's bytecode importer under the hood. Here the evaluator config owns class acquisition; in the separate native ArchUnit recipe, the consuming test imports those classes up front.

How it fits together

ESLint and SonarJS reports are not prerequisites for this gate. AtlasArc.io CI's native enforcement is cycle-only.

1. Add the JUnit dependency

AtlasArc's Apache-2.0 JUnit adapter is available from Maven Central as io.atlasarc:atlasarc-junit:1.4.0. Add it in test scope beside your project's JUnit 5 dependency:

// build.gradle.kts
dependencies {
    testImplementation("io.atlasarc:atlasarc-junit:1.4.0")
}
<dependency>
  <groupId>io.atlasarc</groupId>
  <artifactId>atlasarc-junit</artifactId>
  <version>1.4.0</version>
  <scope>test</scope>
</dependency>

2. Configure current evidence

For a module-less Java/Kotlin project, save this as .atlasarc/evaluator.json. The owning build must compile target/classes before the test runs:

{
  "$schema": "https://atlasarc.io/schemas/evaluator-config-v1.schema.json",
  "configVersion": 1,
  "repositoryRoot": "..",
  "sources": [{
    "id": "jvm:whole-project",
    "backend": "jvm-bytecode",
    "classDirectories": [{"path": "target/classes"}],
    "sourceRoots": [{"path": "src/main/java"}]
  }]
}

Kotlin projects add their real Kotlin source roots. Multi-module projects label each class/source-root pair with the same stable module name used by the IDE. Do not mix named and unlabelled roots.

For TypeScript, generate dependency-cruiser JSON before the JUnit task and add a second source:

{
  "id": "typescript:frontend",
  "backend": "typescript-artifact",
  "root": ".",
  "dependencyCruiserJson": ".atlasarc/depgraph.json"
}

Keep both source objects in the same sources array when one test should own the complete mixed-stack verdict. If source files are newer than their configured bytecode or dependency graph, the assertion fails as invalid instead of reporting a false clean result.

3. Add one ordinary JUnit test

import io.atlasarc.junit.AtlasArcGovernanceAssertions;
import org.junit.jupiter.api.Test;

class CycleGovernanceTest {
    @Test
    void repositoryCycleGovernance() {
        AtlasArcGovernanceAssertions.assertGovernance();
    }
}

The no-argument form resolves .atlasarc/evaluator.json from the test working directory. Pass Path.of("path/to/evaluator.json") when the config lives elsewhere. A second Path argument can set the current directory explicitly for multi-module test tasks.

4. Run the normal test task

Run ./gradlew test, mvn test, or the same task in CI. Ensure any dependency-cruiser generation task runs first. The assertion behaves as follows:

Evaluator outcomeJUnit result
CleanTest passes
Unaccepted cycleAssertion fails with the cycle summary
Invalid config, governance, scope, or stale evidenceAssertion fails closed with the evaluator diagnostic
Internal evaluator errorAssertion fails with the available error detail

The assertion is read-only. It never creates a cycle-debt baseline or writes cycles.json.

When to choose another integration

Related

See Govern cycle exceptions for IDE authoring and stale-record repair, or AtlasArc.io CI for evaluator configuration, baseline adoption, output formats, and exit codes.