It seems in global mode the expectations have to come from a single process:
defmodule Mocker do
import Mox
def init(args) do
set_mox_global()
MyApp.CalcMock
|> expect(:add, fn x, y -> x + y end)
|> expect(:mult, fn x, y -> x * y end)
{:ok, args}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
end
defmodule Adder do
def init(args),
do: {:ok, args}
def handle_call({:add, term1, term2}, _from, state) do
sum = MyApp.CalcMock.add(term1, term2)
{:reply, {:ok, sum}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def add(adder_id, arg1, arg2),
do: GenServer.call(adder_id, {:add, arg1, arg2})
end
defmodule Multiplier do
def init(args),
do: {:ok, args}
def handle_call({:multiply, factor1, factor2}, _from, state) do
product = MyApp.CalcMock.mult(factor1, factor2)
{:reply, {:ok, product}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def multiply(multiplier_id, arg1, arg2),
do: GenServer.call(multiplier_id, {:multiply, arg1, arg2})
end
defmodule MyAppTest do
use ExUnit.Case
setup_all do
{:ok, _mocker_id} = start_supervised(Mocker)
{:ok, adder_id} = start_supervised(Adder)
{:ok, multiplier_id} = start_supervised(Multiplier)
%{
adder_id: adder_id,
multiplier_id: multiplier_id
}
end
test "Use add expectation", context do
assert Adder.add(context.adder_id, 2, 3) == {:ok, 5}
end
test "Use mult expectation", context do
assert Multiplier.multiply(context.multiplier_id, 2, 3) == {:ok, 6}
end
end
Managing multiple sets of mocks with explicit allowances is a bit more work:
defmodule MockShare do
import Mox
def init(args),
do: {:ok, args}
def handle_call({:allow, module, pid}, _from, state) do
Mox.allow(module, self(), pid)
{:reply, :ok, state}
end
def handle_call({:expect, expect_fun}, _from, state) do
expect_fun.()
{:reply, :ok, state}
end
# ---
def child_spec(_args) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
end
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def allow(share_id, module, pid),
do: GenServer.call(share_id, {:allow, module, pid})
def expect(share_id, expect_fun),
do: GenServer.call(share_id, {:expect, expect_fun})
end
defmodule Adder do
def init(args),
do: {:ok, args}
def handle_call({:add, term1, term2}, _from, state) do
sum = MyApp.CalcMock.add(term1, term2)
{:reply, {:ok, sum}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def add(adder_id, arg1, arg2),
do: GenServer.call(adder_id, {:add, arg1, arg2})
end
defmodule Multiplier do
def init(args),
do: {:ok, args}
def handle_call({:multiply, factor1, factor2}, _from, state) do
product = MyApp.CalcMock.mult(factor1, factor2)
{:reply, {:ok, product}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def multiply(multiplier_id, arg1, arg2),
do: GenServer.call(multiplier_id, {:multiply, arg1, arg2})
end
defmodule MyAppTest do
use ExUnit.Case
import Mox, only: [expect: 3]
setup_all do
{:ok, add_share_id} = start_supervised(MockShare, id: :add_share)
MockShare.expect(
add_share_id,
fn ->
expect(MyApp.CalcMock, :add, fn x, y -> x + y end)
end
)
{:ok, mult_share_id} = start_supervised(MockShare, id: :mult_share)
MockShare.expect(
mult_share_id,
fn ->
expect(MyApp.CalcMock, :mult, fn x, y -> x * y end)
end
)
{:ok, adder_id} = start_supervised(Adder)
MockShare.allow(add_share_id, MyApp.CalcMock, adder_id)
{:ok, multiplier_id} = start_supervised(Multiplier)
MockShare.allow(mult_share_id, MyApp.CalcMock, multiplier_id)
%{
adder_id: adder_id,
multiplier_id: multiplier_id
}
end
test "Use add expectation", context do
assert Adder.add(context.adder_id, 2, 3) == {:ok, 5}
end
test "Use mult expectation", context do
assert Multiplier.multiply(context.multiplier_id, 2, 3) == {:ok, 6}
end
end
Getting setup :set_mox_global
to work:
defmodule Adder do
def init(args),
do: {:ok, args}
def handle_call({:add, term1, term2}, _from, state) do
sum = MyApp.CalcMock.add(term1, term2)
{:reply, {:ok, sum}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def add(adder_id, arg1, arg2),
do: GenServer.call(adder_id, {:add, arg1, arg2})
end
defmodule Multiplier do
def init(args),
do: {:ok, args}
def handle_call({:multiply, factor1, factor2}, _from, state) do
product = MyApp.CalcMock.mult(factor1, factor2)
{:reply, {:ok, product}, state}
end
# ---
def child_spec(_args),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
restart: :permanent,
shutdown: 5000,
type: :worker
}
def start_link(),
do: GenServer.start_link(__MODULE__, [])
def multiply(multiplier_id, arg1, arg2),
do: GenServer.call(multiplier_id, {:multiply, arg1, arg2})
end
defmodule MyAppTest do
use ExUnit.Case
import Mox
setup_all do
{:ok, adder_id} = start_supervised(Adder)
{:ok, multiplier_id} = start_supervised(Multiplier)
%{
adder_id: adder_id,
multiplier_id: multiplier_id
}
end
setup :set_mox_global
test "Use add expectation", context do
expect(MyApp.CalcMock, :add, fn x, y -> x + y end)
assert Adder.add(context.adder_id, 2, 3) == {:ok, 5}
end
test "Use mult expectation", context do
expect(MyApp.CalcMock, :mult, fn x, y -> x * y end)
assert Multiplier.multiply(context.multiplier_id, 2, 3) == {:ok, 6}
end
end
Seems what is happening is that setup :set_mox_global
makes the individual test the global owner for the duration of the test - consequently that testing process would have to register all the expectations that are to be shared for the scope of that particular test.