Skip to content

Instantly share code, notes, and snippets.

@krisleech
Last active October 20, 2025 11:32
Show Gist options
  • Select an option

  • Save krisleech/a33e99b17f124c53aca870db13816103 to your computer and use it in GitHub Desktop.

Select an option

Save krisleech/a33e99b17f124c53aca870db13816103 to your computer and use it in GitHub Desktop.
Switchable Elixir adapter

Proposal to allow adapters to be switched at runtime in tests.

Example:

We have an EntityStore which has functions to save and find entities.

In our tests we want the ability to use different implimentations (real, dummy, in-memory etc.) in different tests.

We might want to use the real adapter in acceptance tests, a dummy one just to assert the adapter is called, and an in-memory adapter for all other tests (because it's faster).

  • real - the one used in production, persists to postgres
  • dummy - sends a message when each function is called so test can assert_received to check the function was called, doesn't actually store any data
  • in-memory - same as real but uses in-memory data store

(potentially the dummy and in-memory could be combined in to one adapter)

Each adapter would impliment the same behaviour.

To switch between adatpers in tests, at runtime, we might have a "switchable" adapter which is an adatper which delegates to another adapter, which can be changed at runtime. The switching of an adapter will not be global but only effect the test process.

# In config/dev.exs
config :shared, :entity_store, Shared.EntityStore.Persistent # real

# In config/test.exs
config :shared, :entity_store, Shared.EntityStore.Switchable # switchable (can switch to any adapter)

# In config/prod.exs
config :shared, :entity_store, Shared.EntityStore.Persistent # real

When using the entity store we would do so in the normal way:

defmodule SomeHandler do
  @entity_store Application.get_env(:shared, :entity_store)
  
  def call(...) do
    @entity_store.get(...)
  end
end

Adapters

Persistent

This is the production adapter and stores data in postgres.

defmodule Shared.EntityStore.Persistent do
  @behaviour Behaviours.EntityStore
  
  # ...
end

Switchable

This entity store allows switching between different entity store implimentations, and delegates to whichever is currenly being used.

defmodule EntityStore.Switchable do
  @behaviour Behaviours.EntityStore
  
  @default_adapter @entity_store Application.get_env(:shared, :entity_store)
  
  def use(adapter), do: Process.set(:adapter, adatper)
  def using(), do: Process.get(:adatper, @default_adapter)
  
  def get(id) do
    using().get(id)
  end
  
  # ...
end

(we would proberbly use ProcessTree.get so this works even if further processes are spawned from the test)

InMemory

defmodule EntityStore.InMemory do
  @behaviour Behaviours.EntityStore
  
  def get(id), do: ....
end

This could use a list of map in the Process to store state.

in our tests

we can switch to any of the adapters we like...

setup, do: EntityStore.Switchable.use(InMemory)

EntityStore.Switchable.use(Dummy)
EntityStore.Switchable.use(Persistent)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment