How to Test Applications Using ElixirCache
View SourceThis guide explains how to effectively test applications that use ElixirCache, focusing on the sandbox functionality.
Using the Sandbox Mode
ElixirCache provides a sandbox mode that gives each test its own isolated cache namespace. This ensures your tests:
- Are isolated from each other
- Don't leave lingering cache data between test runs
- Can run in parallel without conflicts
Configuring Your Cache
Use sandbox?: Mix.env() === :test on your cache module. The adapter stays the
same in every environment — the sandbox wraps whatever adapter you choose:
defmodule MyApp.Cache do
use Cache,
adapter: Cache.Redis,
name: :my_app_cache,
opts: [uri: "redis://localhost:6379"],
sandbox?: Mix.env() === :test
endWhen sandbox? is true, Cache.Sandbox is used as the adapter automatically.
You do not need to switch adapters between environments.
Setting Up the Sandbox Registry
Add Cache.SandboxRegistry.start_link() to your test/test_helper.exs:
# In test/test_helper.exs
Cache.SandboxRegistry.start_link()
ExUnit.start()Using the Sandbox in Tests
Register your cache in each test's setup block with
Cache.SandboxRegistry.start/1. This starts the cache supervisor and
registers the current test process for isolation:
defmodule MyApp.CacheTest do
use ExUnit.Case, async: true
setup do
Cache.SandboxRegistry.start(MyApp.Cache)
:ok
end
test "stores and retrieves values" do
assert :ok === MyApp.Cache.put("key", "value")
assert {:ok, "value"} === MyApp.Cache.get("key")
end
test "each test is isolated" do
assert {:ok, nil} === MyApp.Cache.get("key")
end
endUsing Cache.CaseTemplate
For applications with many test files, repeating Cache.SandboxRegistry.start/1 in every
setup block quickly becomes tedious. Cache.CaseTemplate lets you define a single
CacheCase module that automatically starts the right caches for every test that uses it.
Create a CacheCase module
Define a CacheCase module in your application's test support directory:
# test/support/cache_case.ex
defmodule MyApp.CacheCase do
use Cache.CaseTemplate, default_caches: [MyApp.UserCache, MyApp.SessionCache]
endOr let Cache.CaseTemplate discover caches at runtime by inspecting a running supervisor:
defmodule MyApp.CacheCase do
use Cache.CaseTemplate, supervisors: [MyApp.Supervisor]
endWhen :supervisors is used, Cache.CaseTemplate finds the Cache supervisor child at
test setup time and returns all cache modules started under it. This keeps your test setup
in sync with production automatically.
Use the CacheCase in test files
defmodule MyApp.UserTest do
use ExUnit.Case, async: true
use MyApp.CacheCase
test "caches are isolated per test" do
assert {:ok, nil} = MyApp.UserCache.get("key")
assert :ok = MyApp.UserCache.put("key", "value")
assert {:ok, "value"} = MyApp.UserCache.get("key")
end
endTo start additional caches only for a specific test file, pass them via :caches:
defmodule MyApp.AdminTest do
use ExUnit.Case, async: true
use MyApp.CacheCase, caches: [MyApp.AdminCache]
test "admin cache is also started" do
assert {:ok, nil} = MyApp.AdminCache.get("key")
end
endAvailable options
For use Cache.CaseTemplate (when defining a CacheCase module):
:default_caches— list of cache modules to start for every test:supervisors— list of supervisor atoms; theirCachechildren are discovered at runtime
For use MyApp.CacheCase (in a test file):
:caches— additional cache modules for this test file only:sleep— milliseconds to sleep after starting caches (default:10)
Duplicate detection
If the same cache module appears in both :default_caches and :caches, Cache.CaseTemplate
raises at setup time with a clear error listing the duplicates, so collisions are caught early.
Tips for Testing with ElixirCache
- Always use
sandbox?: Mix.env() === :test: Keep the same adapter everywhere — the sandbox handles isolation. - Use
Cache.CaseTemplatefor apps with many test files to avoid repeating setup boilerplate. - Use
Cache.SandboxRegistry.start/1in setup for individual test files that don't share aCacheCase. - Tests can be
async: true: Each test gets its own sandbox namespace. - Test edge cases: Cache misses, errors, and TTL expiration.
- Verify telemetry events: If your application relies on cache metrics.