A repository of bitesize articles, tips & tricks
(in both English and French) curated by Mirego’s team.

Testing child process with mocked dependency in Elixir

Problem

If you try to run the test suite after adding a child process that has a mocked dependency, you will end up with something like this:

** (UndefinedFunctionError) function MyModule.Mock.function/0 is undefined (module MyModule.Mock is not available)

The reason is that the process is started before the mock is defined. We could move the mock definition at compile-time, but we would end up with something like this anyway:

 ** (Mox.UnexpectedCallError) no expectation defined for MyModule.Mock.function/0 in process #PID<0.1029.0> with args []

This time, the reason is that the expectations are process based. The child process does not have access to the mock.

Solution

First, only inject the process in non-test environments. In my case, it was a GenServer. Simply use config files to achieve it:

application.ex
children = Application.get_env(:app, :children)
config/config.exs
config :app, :children, [MyApp.GenServer]
config/test.exs
config :app, :children, []

Now you will need to configure your test to allow access to the mock. Then you can manually start you process.

my_app/gen_server_test.exs
defmodule MyApp.GenServerTest do
  # An ExUnit case where tests use Mox in global mode cannot be async: true.
  use ExUnit.CaseTemplate, async: false

  setup do
    # https://hexdocs.pm/mox/Mox.html#set_mox_global/1
    # In global mode, mocks can be consumed by any process.
    Hammox.set_mox_global()
    Hammox.verify_on_exit!()
  end

  test "test" do
    Hammox.expect(MyModule.Mock, :function, fn ->
      ...
    end)

    start_supervised!(MyApp.GenServer)

    ...
  end
end

Happy testing! 🎉