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

We’re hiring!

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! 🎉