Best way of doing an integration test that spans a Task.start?

You should be running your tasks under a supervisor, so another approach would be something like this:

defmodule MyApp.SomeSupervisor do
  def start_some_task(fun, args) do
    Task.Supervisor.start_child(__MODULE__, ...)
  end

  # For tests: find all children spawned by this supervisor and wait until they finish.
  def wait_for_completion() do
    pids = Task.Supervisor.children(__MODULE__)
    Enum.each(pids, &Process.monitor/1)
    wait_for_pids(pids)
  end

  defp wait_for_pids([]), do: nil

  defp wait_for_pids(pids) do
    receive do
      {:DOWN, _ref, :process, pid, _reason} -> wait_for_pids(List.delete(pids, pid))
    end
  end
end

In your test, call MyApp.SomeSupervisor.wait_for_completion(). It’s not perfect, since it might needlessly wait for tasks not related to your particular tests, but it my case worked good enough.

4 Likes