Set module attributes at runtime

Can this be done? I have module attributes I’m using to distinguish between mocks and my live modules during tests. These attributes read from my configuration files. However, during integration tests, I want to override these default configurations to use the live modules.

My problem is the module attributes are set at compile time and I am overriding them using Application.put_env/4 during the setup of my tests, which is at runtime. The overrides are never persisted in the module attributes and my integration test don’t work.

I use the following setup for accessing mocks:

# config/dev.exs
config :my_app, Adapters,
  my_module: MyApp.MyModule.Live

# config/test.ex
config :my_app, Adapters,
  my_module: MyApp.MyModule.Mock

# usage
def MyApp.MyOtherModule do

  @my_app Application.get_env(:my_app, Adapters)

  def fun(args), do: @my_app[:my_module].my_function(args)

end

Has anyone dealt with this issue before? How do you deal with integration testing in this type of scenario?

1 Like

No. This should not be done like that. Module attributes are compile-time only.

The only way I can see you can do it is using hot code reloading. But it probably is too much hassle to do so in your case.

3 Likes

Module attributes are by definition, compile-time only. To achieve your overall goal you could either not use them, and instead have a function that looks up Application.get_env/2 at runtime or else define a new config/integration_test.ex file setting it differently than test

1 Like

I see. Would love some guidance on how to approach this issue in the most idiomatic way.

Nice. Thanks for the advice! For your latter suggestion, how would you import an alternate config file based on the test you are running? Are there Mix.env values other than :prod, :dev, :test?

I can’t dig in to give a fuller example until after work today, but I just did a quick test. If you do
MIX_ENV=integration mix test
I get an error saying it can’t find a config/integration.exs file
I created the file with contents

IO.puts "ohai from integration env"
[]

Then MIX_ENV=integration mix test
prints the message, compiles and run tests. (The file is expected to return a keyword list, so that’s why the empty list)

You’ll probably want to take it to the next level and use different exunit tags for integration tests, plus perhaps mix aliases to simplify those settings, but that can come after you get the basics going.

1 Like

Excellent! Thanks for the example, I’m going to see what I can get set up this way.