Phoenix: ErrorView test fails after upgrading to 1.3

Created on 11 Aug 2017  路  7Comments  路  Source: phoenixframework/phoenix

Environment

  • Elixir version (elixir -v): 1.5
  • Phoenix version (mix deps): 1.3
  • Operating system: OS X

Expected behavior

Here is test that I'm trying to get working again (it did on Phoenix 1.2):

  alias MyApp.ErrorView
  import Phoenix.View

  # ..

  test "render any error" do
    assert render(ErrorView, "505.html", []) ==
           "Internal server error"
    assert render(ErrorView, "505.json", []) ==
           %{error: "Internal server error", status: 500}
  end

Error view looks like this:

defmodule MyApp.ErrorView do
  use MyApp.Web, :view

  # ...

  def render("500.json", assigns) do
    %{error: render("500.html", assigns), status: 500}
  end

  def render("500" <> _, _assigns) do
    "Internal server error"
  end

  def template_not_found(<<_status::binary-size(3), format::binary>>, assigns) do
    render("500" <> format, assigns)
  end
  def template_not_found(_template, assigns) do
    render "500.html", assigns
  end
end

I expect test to trigger template_not_found/2 function because there are no render for 505.json.

Actual behavior

1) test render any error (MyApp.Tests.ErrorViewTest)
     test/my_app/views/error_view_test.exs:41
     ** (FunctionClauseError) no function clause matching in MyApp.ErrorView.render/2

     The following arguments were given to MyApp.ErrorView.render/2:

         # 1
         "505.html"

         # 2
         %{view_module: MyApp.ErrorView, view_template: "505.html"}

     Attempted function clauses (showing 7 out of 7):

         def render("404.json", assigns)
         def render("404.html", %{reason: %Ecto.NoResultsError{}})
         def render("404.html", _assigns)
         def render("400.json", assigns)
         def render("400.html", _assigns)
         def render("500.json", assigns)
         def render(<<"500"::binary(), _::binary()>>, _assigns)

     code: assert render(ErrorView, "505.html", []) ==
     stacktrace:
       (my_app) lib/my_app/views/error_view.ex:4: MyApp.ErrorView.render/2
       test/my_app/views/error_view_test.exs:42: (test)

Most helpful comment

Is it possible for your to extract these files into a minimal repo that replicates this? That would be super helpful to try to track this down. Thanks!

All 7 comments

Can you push a minimal app to a repo that reproduces the issue? From the provided code it looks like it should work as you describe. It would also be helpful if you could include your entire, exact ErrorView module

Full tests code:

defmodule MyApp.ErrorViewTest  do
  use MyApp.ConnCase, async: true
  alias MyApp.ErrorView
  import Phoenix.View

  test "renders 404.html" do
    assert render(ErrorView, "404.html", []) ==
           "Page not found"
  end

  test "render 500.html" do
    assert render(ErrorView, "500.html", []) ==
           "Internal server error"
  end

  test "renders 404.json" do
    assert render(ErrorView, "404.json", []) ==
           %{
             error:  "Page not found",
             status: 404
           }
  end

  test "renders 404.json with ecto error" do
    assigns = [reason: %Ecto.NoResultsError{}]
    assert render(ErrorView, "404.json", assigns) ==
      %{
        error:  "resource not found",
        status: 404
      }
  end

  test "render 500.json" do
    assert render(ErrorView, "500.json", []) ==
           %{
             error:  "Internal server error",
             status: 500
           }
  end

  test "render any error" do
    assert render_to_string(ErrorView, "505.html", []) ==
           "Internal server error"
    assert render_to_string(ErrorView, "505.json", []) ==
           %{error: "Internal server error", status: 500}
  end
end

Full view code:

defmodule MyApp.ErrorView do
  use MyApp.Web, :view

  def render("404.json", assigns) do
    %{error: render("404.html", assigns), status: 404}
  end

  def render("404.html", %{reason: %Ecto.NoResultsError{}}) do
    "resource not found"
  end

  def render("404.html", _assigns) do
    "Page not found"
  end

  def render("400.json", assigns) do
    %{error: render("400.html", assigns), status: 400}
  end

  def render("400.html", _assigns) do
    "bad request"
  end

  def render("500.json", assigns) do
    %{error: render("500.html", assigns), status: 500}
  end

  def render("500" <> _, _assigns) do
    "Internal server error"
  end

  def template_not_found(<<_status::binary-size(3), format::binary>>, assigns) do
    render("500" <> format, assigns)
  end
  def template_not_found(_template, assigns) do
    render "500.html", assigns
  end
end

I can't recreate this using your view and test code. Are you sure that you aren't referencing an old view module, i.e. alias MyApp.ErrorView is in your code. Maybe you renamed this file/module to MyAppWEb.ErrorView and either kept the old file, or your _build contains the old module? Can you verify no old module/file exists, and if so, blow away build to be sure, and retry? Thanks!

Unfortunately, nothing helps:

  1. I've removed _build entirely
  2. I've tried to remove all alias/import usages and test it explicitly.
  3. There are no other modules with this name:
Searching 366 files for "ErrorView"

/Users/andrew/Projects/myapp-api/apps/myapp_api/lib/myapp_api/views/error_view.ex:
    1: defmodule MyApp.ErrorView do
    2    use MyApp.Web, :view
    3  

/Users/andrew/Projects/myapp-api/apps/myapp_api/test/myapp_api/views/error_view_test.exs:
    1: defmodule MyApp.ErrorViewTest  do
    2    use MyApp.ConnCase, async: true
    3:   alias MyApp.ErrorView
    4    import Phoenix.View
    5  
    6    test "renders 404.html" do
    7:     assert render(ErrorView, "404.html", []) ==
    8             "Page not found"
    9    end
   10  
   11    test "render 500.html" do
   12:     assert render(ErrorView, "500.html", []) ==
   13             "Internal server error"
   14    end
   15  
   16    test "renders 404.json" do
   17:     assert render(ErrorView, "404.json", []) ==
   18             %{
   19               error:  "Page not found",
   ..
   24    test "renders 404.json with ecto error" do
   25      assigns = [reason: %Ecto.NoResultsError{}]
   26:     assert render(ErrorView, "404.json", assigns) ==
   27        %{
   28          error:  "resource not found",
   ..
   32  
   33    test "render 500.json" do
   34:     assert render(ErrorView, "500.json", []) ==
   35             %{
   36               error:  "Internal server error",
   ..
   40  
   41    test "render any error" do
   42:     assert render(MyApp.ErrorView, "505.html", []) ==
   43             "Internal server error"
   44:     assert render(MyApp.ErrorView, "505.json", []) ==
   45             %{error: "Internal server error", status: 500}
   46    end

10 matches across 2 files

I'll dig deeper and report in case I would find the root cause.

Just in case, view macro looks like this:

  def view do
    quote do
      import Phoenix.View
      import Phoenix.Controller, only: [view_module: 1]
      import MyApp.Router.Helpers

      @view_resource String.to_atom(Phoenix.Naming.resource_name(__MODULE__, "View"))

      @doc "The resource name, as an atom, for this view"
      def __resource__, do: @view_resource
    end
  end

And ConnCase:

defmodule MyApp.ConnCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      use Phoenix.ConnTest

      alias MyApp.Domain.Repo

      import Ecto
      import Ecto.Query, only: [from: 2]

      import MyApp.Router.Helpers
      import MyApp.ConnCase

      @endpoint MyApp.Endpoint
    end
  end
end

Is it possible for your to extract these files into a minimal repo that replicates this? That would be super helpful to try to track this down. Thanks!

Closing for now as I'm not able to replicate. If you are able to provide a minimal repo that replicates this, please bump the issue. Thanks!

Was this page helpful?
0 / 5 - 0 ratings