     assert [worker] == workers(queue)
   end
 
-  test "workers restart when a linked process terminates abnormally" do
-    queue = :erlang.unique_integer
-
-    :ok = Honeydew.start_queue(queue)
-    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
-
-    [worker] = workers(queue)
-
-    fn -> spawn_link(fn -> raise "intentional crash" end) end |> Honeydew.async(queue)
-    Process.sleep(200)
-
-    assert [worker] != workers(queue)
-  end
-
-  test "workers restart after a job crashes" do
-    queue = :erlang.unique_integer
-
-    :ok = Honeydew.start_queue(queue)
-    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
-
-    [worker] = workers(queue)
-
-    fn -> raise "intentional crash" end |> Honeydew.async(queue)
-    Process.sleep(100)
-
-    [new_worker] = workers(queue)
-
-    assert worker != new_worker
+  test "when a job crashes with an exception" do
+    queue = :erlang.unique_integer
+
+    :ok = Honeydew.start_queue(queue, failure_mode: {EchoingFailureMode, [self()]})
+    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
+
+    [worker] = workers(queue)
+
+    fn -> raise "intentional crash" end
+    |> Honeydew.async(queue)
+
+    assert {%RuntimeError{message: "intentional crash"}, stacktrace} =
+      EchoingFailureMode.await_job_failure_reason()
+    assert is_list(stacktrace)
+
+    Process.sleep(100)
+
+    assert_worker_restarted(queue, worker)
+  end
+
+  test "when a job crashes with an uncaught throw" do
+    queue = :erlang.unique_integer
+    thrown = :ice_cream_cone
+
+    :ok = Honeydew.start_queue(queue, failure_mode: {EchoingFailureMode, [self()]})
+    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
+
+    [worker] = workers(queue)
+
+    fn -> throw(thrown) end
+    |> Honeydew.async(queue)
+
+    assert {^thrown, stacktrace} =
+      EchoingFailureMode.await_job_failure_reason()
+    assert is_list(stacktrace)
+
+    Process.sleep(100)
+
+    assert_worker_restarted(queue, worker)
+  end
+
+  test "when a job is terminated" do
+    queue = :erlang.unique_integer
+
+    :ok = Honeydew.start_queue(queue, failure_mode: {EchoingFailureMode, [self()]})
+    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
+
+    [worker] = workers(queue)
+
+    fn ->
+      Process.sleep(:infinity)
+    end
+    |> Honeydew.async(queue)
+
+    # Without Process.sleep, the failure reason becomes :noproc because the
+    # worker is killed before the job monitor can monitor it
+    Process.sleep(100)
+
+    Process.exit(worker, :kill)
+
+    assert :killed = EchoingFailureMode.await_job_failure_reason()
+
+    Process.sleep(100)
+
+    assert_worker_restarted(queue, worker)
+  end
+
+  test "when a linked process terminates abnormally" do
+    queue = :erlang.unique_integer
+
+    :ok = Honeydew.start_queue(queue, failure_mode: {EchoingFailureMode, [self()]})
+    :ok = Honeydew.start_workers(queue, Stateless, num: 1)
+
+    [worker] = workers(queue)
+
+    fn -> spawn_link(fn -> raise "intentional crash" end) end
+    |> Honeydew.async(queue)
+
+    # # TODO: enable this when https://github.com/koudelka/honeydew/pull/56 is
+    # # fixed
+    # assert {%RuntimeError{message: "intentional crash"}, stacktrace} =
+    #   EchoingFailureMode.await_job_failure_reason()
+    # assert is_list(stacktrace)
+
+    Process.sleep(100)
+
+    assert_worker_restarted(queue, worker)
   end
 
   test "stateful workers reinitialize by default" do

  @moduletag :capture_log

  setup :restart_honeydew

  test "queues/0 + stop_queue/1" do
    assert Enum.empty?(Honeydew.queues)

  defp workers(queue) do
    Honeydew.status(queue) |> Map.get(:workers) |> Map.keys
  end

  defp assert_worker_restarted(queue, worker) do
    [new_worker] = workers(queue)

    assert worker != new_worker
  end

end
