I recently ran into a tricky error while testing a GenServer that uses a mock in its init/1.
Maybe this helps others, or maybe someone has cleaner ideas.
The error looked like this:
** (Mox.UnexpectedCallError) no expectation defined for Supex.Sclang.ScPortMock.open/2
in process #PID<0.206.0> with args [{:spawn, "sclang"}, [:binary]]
At first, I tried the usual:
test "Sclang GenServer state ok" do
Supex.Sclang.ScPortMock
|> expect(:open, fn _name, _opts -> Port.open({:spawn, "cat"}, [:binary]) end)
assert {:ok, _pid} = start_supervised(Sclang)
assert %Sclang{} = :sys.get_state(Sclang)
end
But that failed because the GenServer calls the mock inside init/1.
By the time init/1 runs, the supervised process is in a different pid,
so the expectation defined in the test process didn’t apply.
The missing piece was using Mox.allow/3 with a function that resolves the pid later:
test "Sclang GenServer state ok" do
Supex.Sclang.ScPortMock
|> expect(:open, fn _name, _opts -> Port.open({:spawn, "cat"}, [:binary]) end)
Supex.Sclang.ScPortMock
|> allow(self(), fn -> GenServer.whereis(Sclang) end)
assert {:ok, _pid} = start_supervised(Sclang)
assert %Sclang{} = :sys.get_state(Sclang)
end
The key is that allow/3 accepts a function, not just a pid.
When the GenServer invokes the mock in init/1, Mox evaluates the function,
resolves the pid, and lets the call go through.
This finally got rid of the Mox.UnexpectedCallError.






















