(KeyError) key :phoenix_endpoint not found with `ConnCase` used

While continuing my getting used to Elixir testing approaches venture I encountered a “scratch-my-head” type of issue when upon testing a piece of code that calls render(conn, ...) I began receiving the

** (KeyError) key :phoenix_endpoint not found in: %{phoenix_recycled: true, plug_skip_ [...]

The test module does use MyAppWeb.ConnCase, async: true. The ConnCase is unmodified vanilla Phoenix one, which sets @endpoint and builds conn, just like in other test modules.

I can work the error around with

conn = conn |> put_private(:phoenix_endpoint, MyAppWeb.Endpoint)

but this smells to me. Any hints why do I have to do this? And/or what should I do instead?

An example of test giving this error:

test "clears ip address", %{conn: conn, user: user} do
	# GIVEN an inactive user and an IP address of the connection stored in the database
	assert IpAddress |> where([ipa], ipa.ip_address == ^conn.remote_ip) |> Repo.aggregate(:count) == 1
	# WHEN calling user_login_success/2
	conn = Session.user_login_success(conn, user)
	# EXPECT the ip address record to be removed from the database
	assert IpAddress |> where([ipa], ipa.ip_address == ^conn.remote_ip) |> Repo.aggregate(:count) == 0

The offending (triggering error) render() is located in:

defp user_not_activated(conn, user) do
	|> put_session(:email, user.email)
	|> put_view(MyAppWeb.UserView)
	|> render(:not_activated, [conn: conn, activation_resent_at: user.last_activation_sent_at])
	|> halt()

Setup for this block:

setup %{conn: conn} do
	# No idea why the `put_private` is needed. IMHO shouldn't be
	# as the @endpoint is set in the ConnCase, but without it
	# I get: "** (KeyError) key :phoenix_endpoint not found in: %{phoenix_recycled: true, plug_skip_ [...]"
	conn = conn
		   |> put_format("html")
		   |> put_private(:phoenix_endpoint, NvoiceWeb.Endpoint)
	%{user: AccountsFixtures.fixture(:user), conn: conn}

Can you show the test producing that error?

Yessir! Updated the post. And also dug through more documentation to eventually find this one:


which seems to be much further away from the smelly side, while doing at least as good job of dealing with the error.