I am currently struggling to handle config values in mix (particularly when running tests). This is my scenario:
test.exs
file in /config
I'm currently doing it like this (and this doesn't work). Module being tested (simplified):
defmodule Streamex.Client do
@api_region Application.get_env(:streamex, :region)
@api_key Application.get_env(:streamex, :key)
@api_secret Application.get_env(:streamex, :secret)
@api_version "v1.0"
@api_url "api.getstream.io/api"
def full_url(%Request{} = r) do
url = <<"?api_key=", @api_key :: binary>>
end
end
Test:
setup_all do
Streamex.start
Application.put_env :streamex, :key, "KEY"
Application.put_env :streamex, :secret, "SECRET"
Application.put_env :streamex, :secret, ""
end
What happens when running mix test
is that the main module, which sets attributes from those values, throws the following error since it can't find valid values:
lib/streamex/client.ex:36: invalid literal nil in <<>>
I'm still starting so this may seem obvious, but i can't find a solution after reading the docs.
The problem is that you're storing the return value of Application.get_env
in a module attribute, which is evaluated at compile time. If you change the values in your tests, it won't be reflected in the module attribute -- you'll always get the value that's present when mix
compiled that module, which includes evaluating config/config.exs
and all the modules that mix
compiled before compiling that module. The fix is to move the variables that can be changed to a function and call those functions whenever they're used:
defmodule Streamex.Client do
@api_version "v1.0"
@api_url "api.getstream.io/api"
def full_url(%Request{} = r) do
url = <<"?api_key=", api_key :: binary>>
end
def api_region, do: Application.get_env(:streamex, :region)
def api_key, do: Application.get_env(:streamex, :key)
def api_secret, do: Application.get_env(:streamex, :secret)
end
Note that if this is a library and you want the users of the library to be able to configure the values in their config files, you have to use function calls at runtime as the dependencies of an app are compiled before the app.