Error in test for 404 response with %Ecto.NoResultsError


I am working on an existing Phoenix project. So far a lot of 404 pages were handled manually with code that would look like

case Repo.get(Dataset, id) do
    dataset -> / do stuff
    nil -> / answer with a 404

Now I’ve found that there is a way to handle that automatically with phoenix_ecto, using its Plug.Exception. I have added the dependency as described on the project page and am happy because I can now write code like this :

Repo.get!(Dataset, id)

and in case the query fails, a %Ecto.NoResultsError is raised and the error is converted automatically to a 404 response, and I get the desired result.

Now I’m trying to test that behavior using the assert_error_sent macro like this :

    test "unexisting dataset", %{conn: conn} do
      assert_error_sent(404, fn -> get(conn, dataset_path(conn, :details, "yo")) end)

I have reduced the details function to :

  def details(%Plug.Conn{} = conn, %{"slug" => slug}) do
    dataset = Repo.get_by!(Dataset, slug: slug)
    text(conn, "hello #{}")

I run the test with MIX_ENV=test mix test and it fails, telling me that

  1) test details unexisting dataset (TransportWeb.DatasetControllerTest)
     expected error to be sent as 404 status, but got an error with no response from:
     ** (Protocol.UndefinedError) protocol Phoenix.HTML.Safe not implemented for %Ecto.NoResultsError{message: "expected at least one result but got none in query:\n\nfrom d0 in DB.Dataset,\n  where: d0.slug == ^\"yo\"\n"} of type Ecto.NoResultsError (a struct). This protocol is implemented for the following type(s): Phoenix.LiveView.Rendered, Phoenix.LiveView.Comprehension, Atom, DateTime, List, Phoenix.HTML.Form, Integer, Float, BitString, NaiveDateTime, Date, Time, Tuple
     code: assert_error_sent(404, fn -> get(conn, dataset_path(conn, :details, "yo")) end)
       (phoenix_html) lib/phoenix_html/safe.ex:1: Phoenix.HTML.Safe.impl_for!/1
       (phoenix_html) lib/phoenix_html/safe.ex:15: Phoenix.HTML.Safe.to_iodata/1
       (transport) lib/transport_web/templates/error/internal_server_error.html.eex:3: TransportWeb.ErrorView."internal_server_error.html"/1
       (transport) lib/transport_web/templates/layout/app.html.eex:61: TransportWeb.LayoutView."app.html"/1
       (phoenix) lib/phoenix/view.ex:410: Phoenix.View.render_to_iodata/3
       (phoenix) lib/phoenix/controller.ex:729: Phoenix.Controller.__put_render__/5
       (phoenix) lib/phoenix/endpoint/render_errors.ex:77: Phoenix.Endpoint.RenderErrors.instrument_render_and_send/5
       (phoenix) lib/phoenix/endpoint/render_errors.ex:62: Phoenix.Endpoint.RenderErrors.__catch__/5
       (phoenix) lib/phoenix/test/conn_test.ex:235: Phoenix.ConnTest.dispatch/5
       (phoenix) lib/phoenix/test/conn_test.ex:671: Phoenix.ConnTest.wrap_request/1
       (phoenix) lib/phoenix/test/conn_test.ex:625: Phoenix.ConnTest.assert_error_sent/2
       test/transport_web/controllers/dataset_controller_test.exs:15: (test)

If I access the same page manually, I get a 404 response. I can’t understand why Plug.Exception is having no effect when tests are run.

Could somebody help me ?
Thanks !

The stacktrace suggests the plug is working, but something is going wrong in the view - what’s the code in lib/transport_web/templates/error/internal_server_error.html.eex?

Thanks for the reply.

Here is the code of the template :

<section class="error">
  <h1><%= dgettext("errors", "End of the road!") %></h1>
  <h2><%= assigns[:reason] || dgettext("errors", "Try to refresh the page in a few seconds, or contact us.") %></h2>
  <div class="error__background"></div>

If I comment it, and relaunch the test, then I get :

  1) test details unexisting dataset (TransportWeb.DatasetControllerTest)
     expected error to be sent as 404 status, but got 500 from:
     ** (Ecto.NoResultsError) expected at least one result but got none in query:
     from d0 in DB.Dataset,
       where: d0.slug == ^"yo"

So it looks like a 500 is generated, not a 404.
I’m curious, what makes you say that the plug is working ?

Any input on this ?

This link my be helpful.


  test "does not show resource and instead throw error when id is nonexistent", %{conn: conn} do
    conn = get conn, user_path(conn, :show, -1)
    assert conn.status == 404
1 Like