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)
endTests that don't install a handler get the original module's behaviour — zero impact on unrelated tests.
Constraints
- Call
setup/1before tests start (intest_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.ContractFacadeinstead), DoubleDown internals, NimbleOwnership, or Erlang/OTP modules.
See also
DoubleDown.ContractFacade— dispatch facades fordefcallbackcontracts (typed, LSP-friendly, recommended for new code).DoubleDown.BehaviourFacade— dispatch facades for vanilla@behaviourmodules (typed, but no pre_dispatch or combined contract + facade).
Summary
Functions
Dispatch a call through the dynamic facade.
Set up a dynamic dispatch facade for a module.
Check whether a module has been set up for dynamic dispatch.
Functions
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__).
@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.
Check whether a module has been set up for dynamic dispatch.