# `DoubleDown.DynamicFacade`
[🔗](https://github.com/mccraigmccraig/double_down/blob/main/lib/double_down/dynamic_facade.ex#L1)

Dynamic dispatch facades for existing modules.

Enables Mimic-style bytecode interception — replace any module with
a dispatch shim at test time, then use the full `DoubleDown.Double`
API (expects, stubs, fakes, stateful responders, passthrough) without
defining a contract or facade.

## Setup

Call `setup/1` in `test/test_helper.exs` **before** `ExUnit.start()`:

    DoubleDown.DynamicFacade.setup(MyApp.EctoRepo)
    DoubleDown.DynamicFacade.setup(SomeThirdPartyModule)

    ExUnit.start()

## Usage in tests

    setup do
      DoubleDown.Double.fake(MyApp.EctoRepo, DoubleDown.Repo.OpenInMemory)
      :ok
    end

    test "insert then get" do
      {:ok, user} = MyApp.EctoRepo.insert(User.changeset(%{name: "Alice"}))
      assert ^user = MyApp.EctoRepo.get(User, user.id)
    end

Tests that don't install a handler get the original module's
behaviour — zero impact on unrelated tests.

## Constraints

- Call `setup/1` before tests start (in `test_helper.exs`). Bytecode
  replacement is VM-global; calling it during async tests may cause
  flaky behaviour.
- Cannot set up dynamic facades for DoubleDown contracts (use
  `DoubleDown.ContractFacade` instead), DoubleDown internals,
  NimbleOwnership, or Erlang/OTP modules.

## See also

  * `DoubleDown.ContractFacade` — dispatch facades for `defcallback` contracts
    (typed, LSP-friendly, recommended for new code).
  * `DoubleDown.BehaviourFacade` — dispatch facades for vanilla
    `@behaviour` modules (typed, but no pre_dispatch or combined
    contract + facade).

# `dispatch`

```elixir
@spec dispatch(module(), atom(), [term()]) :: term()
```

Dispatch a call through the dynamic facade.

Called by generated shims. Checks NimbleOwnership for a test
handler, falls back to the original module (`Module.__dd_original__`).

# `setup`

```elixir
@spec setup(module()) :: :ok
```

Set up a dynamic dispatch facade for a module.

Copies the original module to a backup (`Module.__dd_original__`)
and replaces it with a shim that dispatches through
`DoubleDown.DynamicFacade.dispatch/3`.

Call this in `test/test_helper.exs` **before** `ExUnit.start()`.
Bytecode replacement is VM-global — calling during async tests may
cause flaky behaviour.

After setup, use the full `DoubleDown.Double` API:

    DoubleDown.Double.fake(MyModule, handler)
    DoubleDown.Double.expect(MyModule, :op, fn [args] -> result end)

Tests that don't install a handler get the original module's
behaviour automatically.

# `setup?`

```elixir
@spec setup?(module()) :: boolean()
```

Check whether a module has been set up for dynamic dispatch.

---

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