Skip to content

Commit 11c5e93

Browse files
authored
Better "no expectation found" error (#174)
1 parent f8234d0 commit 11c5e93

2 files changed

Lines changed: 56 additions & 11 deletions

File tree

lib/mox.ex

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -922,19 +922,22 @@ defmodule Mox do
922922

923923
@doc false
924924
def __dispatch__(mock, name, arity, args) do
925-
case fetch_fun_to_dispatch([self() | caller_pids()], {mock, name, arity}) do
925+
caller_pids = caller_pids()
926+
927+
case fetch_fun_to_dispatch([self() | caller_pids], {mock, name, arity}) do
926928
:no_expectation ->
927929
mfa = Exception.format_mfa(mock, name, arity)
928930

929931
raise UnexpectedCallError,
930-
"no expectation defined for #{mfa} in #{format_process()} with args #{inspect(args)}"
932+
"no expectation found for #{mfa} in #{format_process(caller_pids, true)} " <>
933+
"with args #{inspect(args)}"
931934

932935
{:out_of_expectations, count} ->
933936
mfa = Exception.format_mfa(mock, name, arity)
934937

935938
raise UnexpectedCallError,
936939
"expected #{mfa} to be called #{times(count)} but it has been " <>
937-
"called #{times(count + 1)} in #{format_process()}"
940+
"called #{times(count + 1)} in #{format_process(caller_pids, false)}"
938941

939942
{:remote, fun_to_call} ->
940943
# It's possible that Mox.Server is running on a remote node in the cluster. Since the
@@ -954,14 +957,17 @@ defmodule Mox do
954957
defp times(1), do: "once"
955958
defp times(n), do: "#{n} times"
956959

957-
defp format_process do
958-
callers = caller_pids()
959-
960+
defp format_process(callers, report_dead_callers?) do
960961
"process #{inspect(self())}" <>
961-
if Enum.empty?(callers) do
962-
""
963-
else
964-
" (or in its callers #{inspect(callers)})"
962+
cond do
963+
Enum.empty?(callers) ->
964+
""
965+
966+
report_dead_callers? and Enum.all?(callers, &(not Process.alive?(&1))) ->
967+
" (or in its callers #{inspect(callers)}, all of which are dead)"
968+
969+
true ->
970+
" (or in its callers #{inspect(callers)})"
965971
end
966972
end
967973

test/mox_test.exs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,51 @@ defmodule MoxTest do
294294

295295
test "raises if there is no expectation" do
296296
assert_raise Mox.UnexpectedCallError,
297-
~r"no expectation defined for CalcMock\.add/2.*with args \[2, 3\]",
297+
~r"no expectation found for CalcMock\.add/2.*with args \[2, 3\]",
298298
fn ->
299299
CalcMock.add(2, 3) == 5
300300
end
301301
end
302302

303+
@tag :requires_caller_tracking
304+
test "reports when all callers have died" do
305+
parent = self()
306+
307+
{owner, owner_ref} =
308+
spawn_monitor(fn ->
309+
expect(CalcMock, :add, fn x, y -> x + y end)
310+
311+
Task.async(fn ->
312+
send(parent, {:task_ready, self(), Process.get(:"$callers")})
313+
314+
receive do
315+
:call_mock ->
316+
error =
317+
try do
318+
CalcMock.add(1, 1)
319+
rescue
320+
error in Mox.UnexpectedCallError -> error
321+
end
322+
323+
send(parent, {:mock_error, error})
324+
end
325+
end)
326+
end)
327+
328+
assert_receive {:task_ready, task, [^owner]}
329+
assert_receive {:DOWN, ^owner_ref, :process, ^owner, :normal}
330+
331+
task_ref = Process.monitor(task)
332+
send(task, :call_mock)
333+
334+
assert_receive {:mock_error, error}
335+
assert_receive {:DOWN, ^task_ref, :process, ^task, :normal}
336+
337+
assert error.message ==
338+
"no expectation found for CalcMock.add/2 in process #{inspect(task)} " <>
339+
"(or in its callers [#{inspect(owner)}], all of which are dead) with args [1, 1]"
340+
end
341+
303342
test "raises if all expectations are consumed" do
304343
expect(CalcMock, :add, fn x, y -> x + y end)
305344
assert CalcMock.add(2, 3) == 5

0 commit comments

Comments
 (0)