dart_arch_test library

ArchUnit-inspired architecture testing library for Dart/Flutter.

Write test(...) blocks that enforce architectural rules — dependency direction, layer boundaries, bounded-context isolation, and naming conventions — using a fluent DSL.

Quick start

import 'package:dart_arch_test/dart_arch_test.dart';
import 'package:test/test.dart';

void main() {
  late DependencyGraph graph;

  setUpAll(() async {
    graph = await Collector.buildGraph('/path/to/my_app');
  });

  test('home feature must not import discover feature', () {
    shouldNotDependOn(
      filesMatching('features/home/**'),
      filesMatching('features/discover/**'),
      graph,
    );
  });

  test('no circular dependencies in domain layer', () {
    shouldBeFreeOfCycles(filesMatching('domain/**'), graph);
  });

  test('feature slices are isolated', () {
    defineSlices({
      'home':     'features/home/**',
      'discover': 'features/discover/**',
      'auth':     'features/auth/**',
    })
    .allowDependency('home', 'auth')
    .allowDependency('discover', 'auth')
    .enforceIsolation(graph);
  });

  test('layers only depend downward', () {
    defineLayers({
      'presentation': 'features/**',
      'domain':       'domain/**',
      'data':         'data/**',
    }).enforceDirection(graph);
  });
}

Classes

Collector
Builds (and caches) the dependency graph for all Dart files under a given root path.
CouplingMetrics
Coupling metrics for a single library, following Robert C. Martin's stability/abstractness model.
Freeze
Violation snapshotting — tolerate known violations while catching new ones.
Layers
An ordered list of named architecture layers.
LibrarySelector
A selector that resolves to a concrete set of library URIs from a DependencyGraph.
LibrarySet
Primary implementation: selects libraries whose URI matches a glob pattern.
Metrics
Static helpers for computing Martin coupling metrics over a DependencyGraph.
Slices
A set of named bounded-context slices with explicit allowed cross-slice dependencies.
Violation
A rule violation with a human-readable description.

Functions

allFiles() LibrarySet
Returns a LibrarySet matching all libraries in the graph.
clearContentMatcherCache() → void
Clears the in-process resolution cache used by extending, implementing, and withAnnotation.
defineLayers(Map<String, String> layerDefs) Layers
Defines an ordered list of layers (top-to-bottom / higher-to-lower).
defineOnion(Map<String, String> layerDefs) Layers
Defines an onion/hexagonal architecture (innermost layer first).
defineSlices(Map<String, String> sliceDefs) Slices
Defines bounded-context slices.
difference(LibrarySelector a, LibrarySelector b) LibrarySelector
Returns a LibrarySelector containing libraries in a but not in b.
extending(String superclassName) LibrarySelector
Returns a LibrarySelector matching all libraries that contain at least one class (or mixin) that extends a class named superclassName.
filesMatching(String pattern) LibrarySet
Convenience top-level functions mirroring the Elixir DSL. Returns a LibrarySet for files matching pattern.
freeze(String ruleId, void assertion(), {String? storeDir}) → void
Convenience top-level function delegating to Freeze.freeze.
implementing(String interfaceName) LibrarySelector
Returns a LibrarySelector matching all libraries that contain at least one class that implements an interface named interfaceName.
intersection(LibrarySelector a, LibrarySelector b) LibrarySelector
Returns a LibrarySelector that is the intersection of a and b.
matchesGlob(String pattern, String value) bool
Returns true if value matches the glob pattern.
shouldBeFreeOfCycles(LibrarySelector subject, DependencyGraph graph) → void
Asserts no circular dependencies among libraries in subject.
shouldHaveUriMatching(LibrarySelector subject, String pattern, DependencyGraph graph) → void
Asserts all libraries in subject have URIs matching pattern.
shouldNotBeCalledBy(LibrarySelector object, LibrarySelector callers, DependencyGraph graph, {LibrarySelector? except}) → void
Asserts that no library in callers directly imports any library in object.
shouldNotDependOn(LibrarySelector subject, LibrarySelector object, DependencyGraph graph, {LibrarySelector? except}) → void
Asserts no library in subject directly imports any library in object.
shouldNotExist(LibrarySelector subject, DependencyGraph graph) → void
Asserts that no library matching subject exists in the graph.
shouldNotTransitivelyDependOn(LibrarySelector subject, LibrarySelector object, DependencyGraph graph, {LibrarySelector? except}) → void
Asserts no transitive dependency from subject to any library in object.
shouldOnlyBeCalledBy(LibrarySelector object, LibrarySelector allowedCallers, DependencyGraph graph, {LibrarySelector? except}) → void
Asserts that only libraries in allowedCallers import libraries in object.
shouldOnlyDependOn(LibrarySelector subject, LibrarySelector allowed, DependencyGraph graph, {LibrarySelector? except}) → void
Asserts libraries in subject only import libraries in allowed (plus SDK and other out-of-scope libraries).
union(LibrarySelector a, LibrarySelector b, [LibrarySelector? c, LibrarySelector? d, LibrarySelector? e]) LibrarySelector
Returns a LibrarySelector that is the union of two or more selectors.
uriToPath(String uri) String
Converts a Dart file URI like package:foo/bar/baz.dart to a dot-separated class path like bar.baz (dropping the .dart suffix and the package prefix).
withAnnotation(String annotationName) LibrarySelector
Returns a LibrarySelector matching all libraries that contain at least one top-level declaration carrying the annotation annotationName.

Typedefs

DependencyGraph = Map<String, Set<String>>
A dependency graph: { libUri -> { importedLibUri, ... } }.

Exceptions / Errors

ArchTestFailure
Thrown when one or more architecture violations are found.
FreezeFailure
Thrown when new violations appear that are not in the stored baseline.