How do I set the env at alias??

Hi all,

I am a newbie to Elixir. I try to create an alias for running another group of tests which has its owned environment. It can be run with MIX_ENV specified. May I set the env at alias to have a simpler command? Like api_test:

defp aliases do
  [
    "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
    "ecto.reset": ["ecto.drop", "ecto.setup"],
    "test": ["ecto.create --quiet", "ecto.migrate", "test"],
    "api_test": "test --only api_test:true"
  ]
end
2 Likes
defp aliases do
  [
    …,
    "api_test": &api_test/1
  ]
end

defp api_test(_) do
  Mix.env(:test) # or whatever env you need
  MixTask.run("test" ["--only", "api_test:true"])
end

This is from memory though, can’t test it right now.

6 Likes

Wow… This is a great idea… I will do that.

It failed. It tries to load the live reload server

** (UndefinedFunctionError) function Phoenix.LiveReloader.init/1 is undefined (module 
Phoenix.LiveReloader is not available)
    Phoenix.LiveReloader.init([])
    (plug) lib/plug/builder.ex:302: Plug.Builder.init_module_plug/4
    (plug) lib/plug/builder.ex:286: anonymous fn/5 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:284: Plug.Builder.compile/3
    (plug) expanding macro: Plug.Builder.__before_compile__/1

there’s also this in mix.exs:

def project do
    [
      …,
      preferred_cli_env: [
        coveralls: :test,
        "coveralls.detail": :test,
        "coveralls.post": :test,
        "coveralls.html": :test,
        "test.reset": :test,
        "test.integration": :test
      ]
    ]
  end
3 Likes

After adding the preferred_cli_env, the previous error is gone. But it still not run the test because:
** (Mix) “mix test” is running on environment “api_test”. If you are running tests along another task, please set MIX_ENV explicitly

It looks like I have to use MIX_ENV…

I think you just did the :preferred_cli_env wrong. Can you please show how you set it up?

I use:

def project do
[
  ...
  preferred_cli_env: [
    "api_test": :api_test
  ]
]

end

Since you want to run :api_test in the MIX_ENV=test this needs to be api_test: :test.

no… I want to run :api_test with the MIX_ENV=api_test… It does run it with api_test. But the mix test gives me the warning:

** (Mix) “mix test” is running on environment “api_test”. If you are running tests along another task, please set MIX_ENV explicitly

You are not allowed to call mix test in any other environment than :test, if you want to do it despite all warnings, then you have to set MIX_ENV explicitely. This is kind of saying “Yes, I know what I am doing and I am really sure I want to do this with all of its implcations and possible side effects which in worst case might destroy the world”.

If you really wan’t to use mix test in another environment, then there is no other way than to use the environment variable.

1 Like

Thanks for the information…

I know I am joining the party quite late, but I have seen a way in which you can actually execute an alias with a given ENV set, without having to manually type “MIX_ENV=blabla” before the command.

You can do it like they do in ecto, use a test_with_env function that does a lot of dark magic :smiley: :

defp aliases do
    [
      "test.all": ["test.unit", "test.integration"],
      "test.unit": &run_unit_tests/1,
      "test.integration": &run_integration_tests/1
    ]
  end

  def run_integration_tests(args), do: test_with_env("integration", args)
  def run_unit_tests(args), do: test_with_env("test", args)

  def test_with_env(env, args) do
    args = if IO.ANSI.enabled?(), do: ["--color" | args], else: ["--no-color" | args]
    IO.puts("==> Running tests with `MIX_ENV=#{env}`")

    {_, res} =
      System.cmd("mix", ["test" | args],
        into: IO.binstream(:stdio, :line),
        env: [{"MIX_ENV", to_string(env)}]
      )

    if res > 0 do
      System.at_exit(fn _ -> exit({:shutdown, 1}) end)
    end
  end

You can read more about this in the blog:

Basically, the hardcore part is the command:

System.cmd("mix", ["test" | args],
        into: IO.binstream(:stdio, :line),
        env: [{"MIX_ENV", to_string(env)}]
      )

Which executes a command at system level and executes the given command with any variable you pass it, setting the ENV explicitly but without you having to care about it.

A clever use of aliases IMHO.

2 Likes

What’s the reason for using this “dark magic” over the simple method shown earlier?

Ecto uses such a complex method, because it tests multiple adapter setups, which are meant to work completely independant (postgres/mysql/mssql). If you’re not in a similar situation it can be much simpler.

1 Like

What’s the reason for using this “dark magic” over the simple method shown earlier?

The main reason was that your solution didn’t work for the OP, as he mentioned.
I also encountered the same issue and your solution didn’t work for my case either.

The solution I presented works for all cases, although, as you correctly pointed out, at an increased cost of complexity, also known to us mortals as dark magic :stuck_out_tongue:

I simply wanted to make it clear that you don’t have to explicitly type MIX_ENV=blabla before your mix command if you want to test with an environment other that :test.