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
abut not inb. -
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
aandb. -
matchesGlob(
String pattern, String value) → bool -
Returns
trueifvaluematches the globpattern. -
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
subjecthave URIs matchingpattern. -
shouldNotBeCalledBy(
LibrarySelector object, LibrarySelector callers, DependencyGraph graph, {LibrarySelector? except}) → void -
Asserts that no library in
callersdirectly imports any library inobject. -
shouldNotDependOn(
LibrarySelector subject, LibrarySelector object, DependencyGraph graph, {LibrarySelector? except}) → void -
Asserts no library in
subjectdirectly imports any library inobject. -
shouldNotExist(
LibrarySelector subject, DependencyGraph graph) → void -
Asserts that no library matching
subjectexists in the graph. -
shouldNotTransitivelyDependOn(
LibrarySelector subject, LibrarySelector object, DependencyGraph graph, {LibrarySelector? except}) → void -
Asserts no transitive dependency from
subjectto any library inobject. -
shouldOnlyBeCalledBy(
LibrarySelector object, LibrarySelector allowedCallers, DependencyGraph graph, {LibrarySelector? except}) → void -
Asserts that only libraries in
allowedCallersimport libraries inobject. -
shouldOnlyDependOn(
LibrarySelector subject, LibrarySelector allowed, DependencyGraph graph, {LibrarySelector? except}) → void -
Asserts libraries in
subjectonly import libraries inallowed(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.dartto a dot-separated class path likebar.baz(dropping the.dartsuffix 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.