Skip to content

Instantly share code, notes, and snippets.

@AndrewDryga
Last active December 12, 2022 16:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndrewDryga/b6e1e3662181afe709b575338da0cd6c to your computer and use it in GitHub Desktop.
Save AndrewDryga/b6e1e3662181afe709b575338da0cd6c to your computer and use it in GitHub Desktop.
Overriding Elixir application configuration in test env by using process dictionary
defmodule MyApp.Config do
@moduledoc """
This module provides set of helper functions that are useful when reading application runtime configuration overrides
in test environment.
"""
if Mix.env() != :test do
def maybe_put_env_override(_key, _value), do: :ok
def fetch_env!(app, key), do: Application.fetch_env!(app, key)
else
def maybe_put_env_override(_key, _value) do
_ = Process.put(key, value)
:ok
end
@doc """
Attempts to override application env configuration from one of 3 sources (in this exact order):
* takes it from process dictionary of a current process;
* takes it from process dictionary of a first process in $callers stack;
* takes it from process dictionary of a first process in $ancestors stack.
This function is especially useful when some options (eg. request endpoint) needs to be overridden
in test environment (eg. to send those requests to Bypass).
"""
def fetch_env!(app, key) do
application_env = Application.fetch_env!(app, key)
with :error <- fetch_process_value(key),
:error <- fetch_process_value(get_first_pid_from_pdict_list(:"$callers"), key),
:error <- fetch_process_value(get_first_pid_from_pdict_list(:"$ancestors"), key) do
application_env
else
{:ok, override} -> Keyword.merge(application_env, override)
end
end
defp fetch_process_value(key) do
case Process.get(key) do
nil -> :error
value -> {:ok, value}
end
end
defp fetch_process_value(nil, _key) do
:error
end
defp fetch_process_value(atom, key) when is_atom(atom) do
atom
|> Process.whereis()
|> fetch_process_value(key)
end
defp fetch_process_value(pid, key) do
case :erlang.process_info(pid, :dictionary) do
{:dictionary, pdict} -> Keyword.fetch(pdict, key)
_other -> :error
end
end
defp get_first_pid_from_pdict_list(stack) do
if values = Process.get(stack) do
List.first(values)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment