Packages
double_down
0.63.1
0.69.0
0.68.0
0.66.0
0.65.0
0.64.1
0.64.0
0.63.3
0.63.2
0.63.1
0.63.0
0.62.1
0.61.0
0.60.4
0.60.3
0.60.2
0.60.1
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.1
0.48.0
0.47.2
0.47.1
0.47.0
0.46.3
0.46.2
0.46.1
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.2
0.37.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.1
0.30.0
0.29.0
0.28.1
0.28.0
0.27.0
0.26.0
0.24.0
Builds on the Mox pattern — generates behaviours and dispatch facades from `defcallback` declarations — and adds stateful test doubles powerful enough to test Ecto.Repo operations without a database.
Current section
Files
Jump to
Current section
Files
double_down
README.md
README.md
# DoubleDown
<!-- nav:header:start -->
[Boundaries >](docs/boundaries.md)
<!-- nav:header:end -->
[](https://github.com/mccraigmccraig/double_down/actions/workflows/test.yml)
[](https://hex.pm/packages/double_down)
[](https://hexdocs.pm/double_down/)
DoubleDown is a test-double (mocks and fakes) library for Elixir. It has
multiple zero-cost routes to adding test boundaries to your system, and
goes beyond mocks with stateful test-doubles aka fakes. It includes an
`Ecto.Repo` fake powerful enough to run ExMachina factories without a database.
Tests that exercise database features — constraints, complex queries,
migrations — should continue to run against a real database. But unit tests
that use the database merely as an easy (but slow) way of getting data into
the right place can run without one. DoubleDown's `InMemory` Repo lets you
keep the factory and drop the DB.
## How it works
A function call passes through four logical layers:
```
┌──────────────────────────────────────────────────────┐
│ FUNCTION CALL │
│ MyApp.Repo.insert(changeset) │
└──────────────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 1. CONTRACT │
│ (type-level interface) │
│ │
│ ┌───────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ defcallback │ │ @behaviour │ │ any module │ │
│ │ (explicit) │ │ (explicit) │ │ (implicit) │ │
│ └────────┬──────┘ └──────┬───────┘ └──────┬──────┘ │
└───────────┼───────────────┼────────────────┼─────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────┐
│ 2. FACADE │
│ (generated dispatch functions) │
│ │
│ ┌───────────────┐ ┌────────────────┐ ┌─────────────┐ │
│ │ContractFacade │ │BehaviourFacadde│ │DynamicFacade│ │
│ └────────┬──────┘ └────────┬───────┘ └──────┬──────┘ │
└──────────┼─────────────────┼────────────────┼────────┘
└─────────────────┼────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 3. DISPATCH │
│ (call resolution) │
│ │
│ ┌────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Static │ │ Runtime │ │ Test │ │
│ │ (compile- │ │ Config │ │ Handler │ │
│ │ time, │ │ │ │ │ │
│ │ zero │ │ App.get_env │ │ NimbleOwner- │ │
│ │ overhead) │ │ → apply/3 │ │ ship lookup │ │
│ └─────┬──────┘ └──────┬───────┘ └──────┬───────┘ │
└────────┼────────────────┼─────────────────┼──────────┘
└────────────────┼─────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 4. IMPLEMENTATION │
│ (actual execution) │
│ │
│ ┌──────────────────────────┐ ┌──────────────────┐ │
│ │ Production Module │ │ Test Double │ │
│ │ │ │ (stub / fake / │ │
│ │ │ │ expect) │ │
│ └──────────────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────┘
```
Three facade types let you add boundaries at different levels of control:
| Facade | Contract | Best for |
|-------------------|---------------------------------|-------------------------------------------|
| `ContractFacade` | `defcallback` (explicit, typed) | New code you control |
| `BehaviourFacade` | existing `@behaviour` | Third-party or legacy behaviours |
| `DynamicFacade` | implicit (module's public API) | Adding boundaries without touching source |
Dispatch is uniform across all three contract/facade types — providing the
same resolution mechanism, `DoubleDown.Double` API for tests, and
`DoubleDown.Log` for call tracing.
In production, dispatch overhead can be completely eliminated at compile
time: with `ContractFacade` and `BehaviourFacade`, static dispatch inlines
calls — resulting in zero overhead versus calling the implementation directly,
while with `DynamicFacade` no shims are even generated in production.
## Installation
[](https://hex.pm/packages/double_down)
Add `double_down` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:double_down, "~> 0.63"}
]
end
```
## Documentation
- **[Boundaries](docs/boundaries.md)** — contracts, facades, and the dispatch
mechanism
- **[Dispatch](docs/dispatch.md)** — uniform dispatch resolution
- **[Stateful Doubles](docs/stateful-doubles.md)** — stateful fakes, handler
state, cross-contract access
- **[Double API](docs/double-api.md)** — expect, stub, fallback, verify!,
passthrough
- **[Repo](docs/repo.md)** — the built-in Ecto.Repo contract and its test
doubles
Archived documentation from previous versions lives in
[docs/archive/](docs/archive/) — these cover the same concepts but may use
outdated module names.
## License
MIT License — see [LICENSE](LICENSE) for details.
<!-- nav:footer:start -->
---
[Boundaries >](docs/boundaries.md)
<!-- nav:footer:end -->