Opps Sunday afternoon programming escalation:
defmodule Tracer do
@moduledoc false
import ExUnit.Assertions, only: [assert: 1]
def start_link do
Agent.start_link(fn -> %{expectations: %{}, traces: %{}} end, name: __MODULE__)
:dbg.start()
:dbg.tracer(
:process,
{fn {:trace, _pid, :call, {module, function, args}}, _state ->
Agent.update(__MODULE__, fn state ->
update_in(state, [:traces, {module, function, length(args)}], fn
nil -> 1
count -> count + 1
end)
end)
end, nil}
)
:dbg.p(:all, :c)
:ok
end
def expect(module, function, arity, count \\ 1) do
Agent.update(__MODULE__, fn state ->
put_in(state, [:expectations, {module, function, arity}], count)
end)
:dbg.tp(module, function, arity, [])
:ok
end
def verify do
%{expectations: expectations, traces: traces} = Agent.get(__MODULE__, & &1)
assert expectations == traces
end
def verify_on_exit! do
ExUnit.Callbacks.on_exit(Tracer, fn -> Tracer.verify() end)
end
end
This module can be used similar to Mox like this:
test "renders list of posts", %{user: user, conn: conn} do
Tracer.expect(MyApp.Posts, :get_posts, 1, count)
# ...
end
Now it properly asserts that this function is called count times:
Assertion with == failed
code: assert expectations == traces
left: %{{MyApp.Posts, : get_posts, 1} => 2}
right: %{{MyApp.Posts, : get_posts, 1} => 1}
Will probably clean it up a little bit more (for example replace the Agent with a proper GenServer and implement nicer error messages) and then publish it on hex
Any suggestions for a nice name?






















