Error in test for 404 response with %Ecto.NoResultsError

Hello,

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
end

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)
    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 #{dataset.id}")
  end

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

  1) test details unexisting dataset (TransportWeb.DatasetControllerTest)
     apps/transport/test/transport_web/controllers/dataset_controller_test.exs:14
     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)
     stacktrace:
       (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>
</section>

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

  1) test details unexisting dataset (TransportWeb.DatasetControllerTest)
     apps/transport/test/transport_web/controllers/dataset_controller_test.exs:14
     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 ?
Thanks

This link my be helpful.

Essentially:

  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
  end
1 Like