Search code examples
unit-testingmockingelixir-mix

Is it possible to have Elixir's Mix load sources from more than one path?


I have been trying like mad to use config based mocks on Elixir. I have defined my mocked module and placed it inside a ".ex" file under the "test/" directory. Then whenever I run "mix test" it fails to load the module. However if I move the mock under "lib/" then everything works just fine. So I was wondering if there is something I'm missing on my configuration and file structure OR if there is a way to tell "mix" to look for source files in another directory in addition to "lib/".

File structure:

my_app/
 |
 + -- lib/
 |     my_lib.ex
 |     my_service.ex
 |
 + ---test/
 |     test_helper.ex
 |     my_service_mock.ex
 |     my_lib_test.exs
 |
 +----config/
       config.exs
       test.exs
       prod.exs
       dev.exs

config/dev.exs

import Config
config :my_app, my_service: MyApp.MyService

config/test.exs

import Config
config :my_app, my_service: MyApp.MyServiceMock

my_lib.ex

defmodule MyLib do
  @my_service Application.get_env(:my_app, :my_service)

  def do_something, do: @my_service.do_something_else
end

my_service.ex

defmodule MyApp.MyService do
  def do_something_else, do: { :ok, "Running business task" }
end

my_service_mock.ex

defmodule MyApp.MyServiceMock do
  def do_something_else, do: { :ok, "Faking business task" }
end

my_lib_test.ex

defmodule MyApp.MyLibTest do
  use ExUnit.Case

  alias MyApp.MyLib

  test "MyList.do_something/0 should do it's thing" do
    assert { :ok, "Faking business task" } = MyLib.do_something
  end
end

The command "mix test" fails with the following error:

== Compilation error in file lib/my_lib.ex ==
** (UndefinedFunctionError) function MyApp.MyServiceMock.do_something_else/0 is undefined (module MyApp.MyServiceMock is not available)
    MyApp.MyServiceMock.do_something_else()
    lib/my_lib.ex:3: (module)
    (stdlib 3.14) erl_eval.erl:680: :erl_eval.do_apply/6

I'm running elixir 1.11.2.


Solution

  • Well, I finally found out the solution on this post on Elixir Forum: https://elixirforum.com/t/load-module-during-test/7400

    It turns out there is a variable in the "Mix.Project" that specifies the paths for the sources:

    So in my "mix.exs" I did the following:

      def project do
        [
          ...
          elixirc_paths: elixirc_paths(Mix.env),
          ...
        ]
      end
    
      defp elixirc_paths(env_name) do
        case env_name do
          :test -> ["lib", "test/mockery"]
           _ -> ["lib"]
        end
      end
    

    Of course I added the directory "test/mockery/" and moved "MyApp.MyServiceMock" there...