Make ExUnit test pass when process fails to start

Background

I have a process that tries to establish a connection to a server and I am testing it. On the happy path, I check that the process does it’s job once it connects.

But its the sad path that is interesting. Should the process fail to connect to the server, I want it to be gracefully terminated and to stop initialization. How do I test this without making the test fail? (the expected behaviour is to have the process die after all)

Code

Worker:

def init(state) do
  Process.flag(:trap_exit, true)
  {:ok, state, {:continue, :establish_conn}}
end

def handle_continue(:establish_conn, state) do
   
    conn_deps = [
      open_fn:      state.deps.http.open,
      await_fn:     state.deps.http.await_up,
      dump_error:   state.deps.dumper.save_failed_request
    ]

    base_url = state.opts.base_url

    with  {:ok, conn_pid} <- Logic.establish_connection(base_url, conn_deps)
    do
      new_state = %{state | conn_pid: conn_pid}
      {:noreply, new_state}
    else
      {:error, reason} -> {:stop, reason, state}
    end
  end

  def terminate(_reason, state) do
    Logic.close_connection(state.conn_pid, [close_fn: state.deps.http.close])
  end

Because connections take a long time to establish, I use handle_continue. However, since in this case the process will fail to connect, handle_continue will return {:stop, reason, state} and prevent the process from continuing, which is expected.

The process then suicides itself and executes the terminate function.

Question

How do I test this without making the tests fail?

Anything wrong with the terminate_wait approach?

So for example you could just stick a monitor on the process and wait for the :DOWN message with a blocking receive with a timeout.

1 Like

Actually, simply using the format:


    test "closes connection if it cannot establish it" do
      Process.flag :trap_exit, true

      args = %{a: 1, b: 2}

      Worker.start_link(args)

      assert_receive {:EXIT, _, :my_awesome_termination_reason_here_in_this_place}
      Process.flag :trap_exit, false
    end

Does the trick. No supervisor needed

2 Likes