The real implementation was a bit more complicated, but this covers the essential idea (untested). The test will call MyConfig.register(app, key, url)
from the before callback (url constructed from the port obtained from bypass). The application code will call MyConfig.get(app, key)
and the lookup call will return an overridden value if set, otherwise, will fall back to the Application.get_env. The lookup can handle Task.spawn etc, since most of them set $callers/$ancestors.
defmodule MyConfig do
def get(app, key) do
if test? do
case lookup({:__config_override, key}, [self()]) do
nil -> Application.get_env(app, key)
value -> value
end
else
Application.get_env(app, key)
end
end
def override(_app, key, value) do
Process.put({:__config_override, key}, value)
end
defp lookup(key, list), do: lookup(key, list, MapSet.new())
defp lookup(_key, [], _visited), do: nil
defp lookup(key, [head | rest], visited) do
if !MapSet.member?(visited, head) do
case __get(head, key) do
nil ->
list = Enum.concat([rest, __get(head, :"$callers", []), __get(head, :"$ancestors", [])])
lookup(key, list, MapSet.put(visited, head))
value ->
value
end
else
lookup(key, rest, visited)
end
end
defp __get(process, key), do: __get(process, key, nil)
defp __get(process, key, default) when is_atom(process) do
__get(Process.whereis(process), key, default)
end
defp __get(process, key, default) do
{:dictionary, dictionary} = Process.info(process, :dictionary)
:proplists.get_value(key, dictionary, default)
end
end