Tracing and asserting function calls

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 :blush: Any suggestions for a nice name?