Testing {:stop, :some_error} in Genserver.init/1 with ExUnit

I’ve used Process.flag(:trap_exit, true) (Process.flag/2) in the past inside the test module - just make sure to purge the resulting :EXIT message from the test process mailbox.

Looking at catch_exit/1 it seems to be designed to catch an exit inside the same process that triggered it with Kernel.exit/1

Example: see test "FrequencySup demo"

defmodule FrequencySupTest do
  use ExUnit.Case

  setup do
    {:ok, sup} = FrequencySup.start_link()

    on_exit fn ->
      refute (Process.alive? sup), "Supervisor still running!"
    end

    [sup: sup]
  end

  defp wait(_, _, left) when left < 1 do
    nil
  end
  defp wait(fun, timeout, left) do
    case fun.() do
      nil ->
        Process.sleep timeout
        wait fun, timeout, (left - 1)
      result ->
        result
    end
  end

  defp whereisFrequency,
    do: Process.whereis Frequency

  defp terminate_wait({pid, terminate}) do
    ref = Process.monitor pid

    terminate.()

    receive do # wait for the supervisor to terminate
      {:DOWN, ^ref, :process, _object, _reason} ->
        Process.demonitor ref, [:flush]
    end
  end

  test "FrequencySup demo", context do
    sup = context[:sup]
    Process.flag :trap_exit, true

    initial_pid = whereisFrequency()
    assert (Kernel.is_pid initial_pid)

    fovl_pid = Process.whereis FreqOverload
    assert (Kernel.is_pid fovl_pid)

    # Kill off frequency
    terminate_wait({initial_pid, (fn -> Process.exit initial_pid, :kill end)})

    # Wait until replacement Frequency is up and running
    freq_pid = wait &whereisFrequency/0, 10, 10
    assert (Kernel.is_pid freq_pid)

    # Use Supervisor.which_children/1
    expected_children = Enum.sort [
      {Frequency, freq_pid, :worker, [Frequency]},
      {FreqOverload, fovl_pid, :supervisor, [FreqOverload]}
    ]
    assert (expected_children == (Enum.sort (Supervisor.which_children sup)))

    # Use Supervisor.count_children/1
    expected_count = %{active: 2, specs: 2, supervisors: 1, workers: 1}
    assert (Map.equal? expected_count, (Supervisor.count_children sup))

    # Now stop the supervisor
    terminate_wait({sup, &FrequencySup.stop/0})
    assert_received {:EXIT, _, :shutdown}
    Process.flag :trap_exit, false
  end

end

I wasn’t writing a unit test as much as I was solidifying my understanding of the API.

2 Likes