SandboxRegistry
We can use the sandbox registry to help create sandboxes for testing
Sandboxes help us test by allow us to specify a mock that will be utilzed only for the specific test, allowing us to modify the return value of a specific function only in test.
We can utilize this pattern by building around an adapter pattern, and using the sandbox in dev mode. Other ways to build this pattern
include using a flag like sandbox? to enable sandbox mode and swap out calls to a sanbox
Example Sandbox
defmodule HTTPSandbox do
@registry :http_sandbox
@keys :unique
def start_link do
Registry.start_link(keys: @keys, name: @registry)
end
def set_get_responses(tuples) do
tuples
|> Map.new(fn {url, func} -> {{:get, url}, func} end)
|> then(&SandboxRegistry.register(@registry, @state, &1, @keys))
|> case do
:ok -> :ok
{:error, :registry_not_started} -> raise_not_started!()
end
# Random sleep is needed to allow registry time to insert
Process.sleep(50)
end
def get_response(url, headers, options) do
func = find!(:get, url)
func.(url, headers, options)
end
def find!(method, url) do
case SandboxRegistry.lookup(@registry, @state) do
{:ok, funcs} ->
find_response!(funcs, method, url)
{:error, :pid_not_registered} ->
raise """
No functions registered for #{inspect(self())}
Action: #{inspect(action)}
URL: #{inspect(url)}
======= Use: =======
#{format_example(action, url)}
=== in your test ===
"""
{:error, :registry_not_started} ->
raise """
Registry not started for #{inspect(__MODULE__)}.
Please add the line:
#{inspect(__MODULE__)}.start_link()
to test_helper.exs for the current app.
"""
end
end
endNow we can use this in an HTTP module only in test by doing
defmodule HTTP do
@defaults_opts [
sandbox?: Mix.env() === :test
]
def get(url, header, opts) do
opts = Keyword.merge(@defaults_opts, opts)
if opts[:sandbox?] do
HTTPSandbox.get_response(url, headers, opts)
else
make_get_request()
end
end
endNow in test we have the ability to mock out our get requests per test
describe "some get request" do
test "test get /url" do
HTTPSandbox.set_get_responses([{
"myurl.com",
fn _url, _headers, _opts -> {:ok, :whatever} end
}])
assert {:ok, :whatever} === HTTP.get("myurl.com", [], [])
end
endInstallation
Available in Hex, the package can be installed
by adding sandbox_registry to your list of dependencies in mix.exs:
def deps do
[
{:sandbox_registry, "~> 0.1.0"}
]
endThe docs can be found at https://hexdocs.pm/sandbox_registry.