ArchTest – Architecture rules as tests. Enforced from bytecode.
ArchTest is an ArchUnit-inspired architecture testing library for Elixir. Rules live in plain ExUnit tests, run with mix test, and produce structured failure output listing every violation — with zero changes to your production code.
The problem it solves
Elixir has great tools, but there’s a gap:
-
Credo catches style issues and code smells within a file — it won’t tell you your domain layer is calling your web layer
-
Boundary gives compile-time warnings for declared boundaries, but requires annotating every module and can’t easily express transitive rules or glob-based selections
-
ArchTest fills the rest: dependency direction, transitive paths, cycle detection, naming conventions, coupling metrics — all in ExUnit, no production code touched
What it does
Write architecture rules as regular tests:
elixir
defmodule MyApp.ArchTest do
use ExUnit.Case
use ArchTest
test "services don't call repos directly" do
modules_matching("MyApp.**.*Service")
|> should_not_depend_on(modules_matching("MyApp.**.*Repo"))
end
test "no circular dependencies" do
modules_matching("MyApp.**") |> should_be_free_of_cycles()
end
test "no Manager modules exist" do
modules_matching("MyApp.**.*Manager") |> should_not_exist()
end
end
Key features
-
Dependency assertions:
should_not_depend_on,should_only_depend_on,should_not_be_called_by,should_only_be_called_by,should_not_transitively_depend_on,should_be_free_of_cycles -
Layered, onion/hexagonal, and modulith/bounded-context architecture enforcement out of the box
-
Flexible module selection with glob patterns,
excluding,union,intersection, andmodules_satisfying/1for custom predicates -
Code conventions: ban
IO.puts,dbg, bareraise, undocumented public functions, and more -
Coupling metrics: instability, abstractness, distance from the main sequence (Martin metrics)
-
Violation freeze for gradual adoption — baseline existing violations and only fail on new ones
Motivation
I built this for my own projects after wanting ArchUnit-style rules in Elixir and finding no equivalent. It works from compiled bytecode, so there’s nothing to annotate and no build step to change.
Links
-
GitHub:
https://github.com/yoavgeva/arch_test
Would love any feedback, especially on the DSL ergonomics and any rule types you’d find useful!






















