# `ArchTest`
[🔗](https://github.com/yoavgeva/arch_test/blob/v0.2.0/lib/arch_test.ex#L1)

ArchUnit-inspired architecture testing library for Elixir.

Write ExUnit tests that enforce architectural rules — dependency direction,
layer boundaries, bounded-context isolation, and naming conventions —
using a fluent, pipe-based DSL.

## Quick start

    defmodule MyApp.ArchitectureTest do
      use ExUnit.Case
      use ArchTest

      test "service modules don't call repos directly" do
        modules_matching("**.*Service")
        |> should_not_depend_on(modules_matching("**.*Repo"))
      end

      test "no Manager modules exist" do
        modules_matching("MyApp.**.*Manager") |> should_not_exist()
      end
    end

## Options for `use ArchTest`

- `:app` — OTP app atom to limit introspection (default: `:all`)
- `:freeze` — `true` to auto-freeze all rules in the module (default: `false`)

## Module reference

- `ArchTest.ModuleSet` — module selection and filtering DSL
- `ArchTest.Assertions` — core assertion functions
- `ArchTest.Layers` — layered architecture enforcement
- `ArchTest.Modulith` — bounded-context / modulith isolation
- `ArchTest.Freeze` — violation baseline / freezing
- `ArchTest.Metrics` — coupling/instability metrics
- `ArchTest.Conventions` — pre-built Elixir convention rules
- `ArchTest.Collector` — BEAM dependency graph builder
- `ArchTest.Pattern` — glob pattern matching

# `all_modules`

```elixir
@spec all_modules() :: ArchTest.ModuleSet.t()
```

Returns a `ModuleSet` matching every module in the application.

# `all_modules_covered_by`

Asserts every module under namespace_pattern belongs to a declared slice.

# `all_modules_covered_by`

Asserts every module under namespace_pattern belongs to a declared slice.

# `allow_dependency`

```elixir
@spec allow_dependency(ArchTest.Modulith.t(), atom(), atom()) :: ArchTest.Modulith.t()
```

Allows `from_slice` to call the public API of `to_slice`.

# `define_layers`

```elixir
@spec define_layers(keyword()) :: ArchTest.Layers.t()
```

Defines an ordered list of architecture layers (top to bottom).

## Example

    define_layers(
      web:     "MyApp.Web.**",
      context: "MyApp.**",
      repo:    "MyApp.Repo.**"
    )
    |> enforce_direction()

# `define_onion`

```elixir
@spec define_onion(keyword()) :: ArchTest.Layers.t()
```

Defines an onion/hexagonal architecture (innermost layer first).

## Example

    define_onion(
      domain:      "MyApp.Domain.**",
      application: "MyApp.Application.**",
      adapters:    "MyApp.Adapters.**"
    )
    |> enforce_onion_rules()

# `define_slices`

```elixir
@spec define_slices(keyword()) :: ArchTest.Modulith.t()
```

Defines bounded-context slices for a modulith architecture.

## Example

    define_slices(
      orders:    "MyApp.Orders",
      inventory: "MyApp.Inventory",
      accounts:  "MyApp.Accounts"
    )
    |> allow_dependency(:orders, :accounts)
    |> enforce_isolation()

# `enforce_direction`

```elixir
@spec enforce_direction(ArchTest.Layers.t()) :: :ok
```

Enforces layer direction (each layer may only depend on layers below it).

# `enforce_isolation`

```elixir
@spec enforce_isolation(ArchTest.Modulith.t()) :: :ok
```

Enforces bounded-context isolation (see `ArchTest.Modulith`).

# `enforce_onion_rules`

```elixir
@spec enforce_onion_rules(ArchTest.Layers.t()) :: :ok
```

Enforces onion architecture rules (dependencies point only inward).

# `excluding`

Excludes modules matching `pattern` from a `ModuleSet`.

## Example

    modules_matching("MyApp.**")
    |> excluding("MyApp.Test.*")

# `intersection`

Returns modules present in both `ModuleSet`s (intersection / AND).

# `modules_in`

```elixir
@spec modules_in(String.t()) :: ArchTest.ModuleSet.t()
```

Returns a `ModuleSet` for all direct children of `namespace`.

Shorthand for `modules_matching("Namespace.*")`.

## Example

    modules_in("MyApp.Orders")
    # equivalent to modules_matching("MyApp.Orders.*")

# `modules_matching`

```elixir
@spec modules_matching(String.t()) :: ArchTest.ModuleSet.t()
```

Returns a `ModuleSet` for modules matching the given glob pattern.

## Pattern semantics

| Pattern | Matches |
|---------|---------|
| `"MyApp.Orders.*"` | Direct children only |
| `"MyApp.Orders.**"` | All descendants at any depth |
| `"MyApp.Orders"` | Exact match only |
| `"**.*Service"` | Last segment ends with `Service` |
| `"**.*Service*"` | Last segment contains `Service` |

## Example

    modules_matching("**.*Controller")
    |> should_not_depend_on(modules_matching("**.*Repo"))

# `modules_satisfying`

```elixir
@spec modules_satisfying((module() -&gt; boolean())) :: ArchTest.ModuleSet.t()
```

Returns a `ModuleSet` matching modules that satisfy a custom predicate.

## Example

    modules_satisfying(fn mod ->
      function_exported?(mod, :__schema__, 1)
    end)
    |> should_reside_under("MyApp.**.Schemas")

# `satisfying`

Applies a custom check function `(graph, module -> [Violation.t()])` to
each module in `subject`.

# `should_be_free_of_cycles`

Asserts no circular dependencies among modules in `subject`.

# `should_export`

Asserts all modules in `subject` export the given function.

# `should_have_attribute`

Asserts all modules in `subject` have the given module attribute.

# `should_have_attribute_value`

Asserts all modules in `subject` have the given attribute with the given value.

# `should_have_module_count`

Asserts that the number of modules matching `subject` satisfies the given constraints.

# `should_have_name_matching`

Asserts that all modules in `subject` have names matching `name_pattern`.

# `should_have_public_functions_matching`

Asserts all modules in `subject` have at least one public function whose name
matches the given glob pattern.

# `should_implement_behaviour`

Asserts that all modules in `subject` implement the given behaviour.

# `should_implement_protocol`

Asserts that all modules in `subject` implement the given protocol.

# `should_not_be_called_by`

Asserts that no module in `callers` calls any module in `object`.

# `should_not_depend_on`

Asserts that no module in `subject` directly depends on modules in `object`.

# `should_not_depend_on_each_other`

```elixir
@spec should_not_depend_on_each_other(ArchTest.Modulith.t()) :: :ok
```

Asserts that slices have absolutely no cross-slice dependencies (strict isolation).

# `should_not_exist`

Asserts that no module in `subject` exists.

# `should_not_export`

Asserts no module in `subject` exports the given function.

# `should_not_have_attribute`

Asserts all modules in `subject` do NOT have the given module attribute.

# `should_not_have_attribute_value`

Asserts all modules in `subject` do NOT have the given attribute with the given value.

# `should_not_have_public_functions_matching`

Asserts no module in `subject` has public functions whose names match the pattern.

# `should_not_implement_behaviour`

Asserts that no module in `subject` implements the given behaviour.

# `should_not_implement_protocol`

Asserts that no module in `subject` implements the given protocol.

# `should_not_transitively_depend_on`

Asserts no transitive dependency from `subject` to modules in `object`.

# `should_not_use`

Asserts no module in `subject` uses the given module (via `use ModuleName`).

# `should_only_be_called_by`

Asserts only modules in `allowed_callers` may call modules in `object`.

# `should_only_depend_on`

Asserts that modules in `subject` only depend on modules in `allowed`.

# `should_reside_under`

Asserts that all modules in `subject` reside under `namespace_pattern`.

# `should_use`

Asserts all modules in `subject` use the given module (via `use ModuleName`).

# `union`

Combines two `ModuleSet`s (union / OR).

## Example

    modules_matching("**.*Service")
    |> union(modules_matching("**.*View"))

---

*Consult [api-reference.md](api-reference.md) for complete listing*
