Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling configuration values in mix

I am currently struggling to handle config values in mix (particularly when running tests). This is my scenario:

  • i have a client library, with some common config values (key, secret, region).
  • i want to test what happens when there's no region value setup
  • i have no 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.

like image 609
sixFingers Avatar asked Oct 28 '25 09:10

sixFingers


1 Answers

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.

like image 175
Dogbert Avatar answered Oct 30 '25 01:10

Dogbert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!