DoubleDown.DynamicFacade (double_down v0.47.2)

Copy Markdown View Source

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).

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(module, operation, args)

@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(module)

@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?(module)

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

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