chensan

chensan

Why Phoenix Recycling?

I’m writing tests for my controller:

  defp log_in_user(%{conn: conn, log_in_as: email}) do
    role = role_fixture()
    user = user_fixture(role, email: email)
    conn = assign(conn, :current_user, user)
    {:ok, conn: conn, user: user}
  end

  defp create_site(%{conn: conn, user: user}) do
    site = site_fixture(user)
    {:ok, site: site}
  end

  describe "delete site" do
    setup [:log_in_user, :create_site]

    @tag log_in_as: "sam"
    test "deletes chosen site", %{conn: conn, site: site} do
      conn = delete(conn, Routes.site_path(conn, :delete, site))
      assert redirected_to(conn) == Routes.site_path(conn, :index)

      assert_error_sent 404, fn ->
        get(conn, Routes.site_path(conn, :show, site))
      end
    end
  end

The weird thing here is conn.assigns.current_user becomes nil in assert_error_sent where it should have value.

I found some similar questions around the web:

  1. Troubleshooting a failed test 302 redirect instead of 200 - #3 by chensan
  2. https://stackoverflow.com/questions/50110449/phoenix-controller-test-case-loses-current-user
  3. https://stackoverflow.com/questions/46363292/losing-conn-assigns-in-the-middle-of-a-test

Seems the problem is caused by Phoenix.ConnTest – Phoenix v1.4.0, and there’s a fix:

      saved_assigns = conn.assigns

      conn =
        conn
        |> recycle()
        |> Map.put(:assigns, saved_assigns)

I don’t quite understand why, it’s so unexpected in our tests. Anyone could help explain it?

Marked As Solved

LostKobrakai

LostKobrakai

recycle does essentially what happens if you start a new request in the browser. Any new request in the browser will start of without any assigns. Usually you’re kept logged in by putting something in the session for the user.

Also Liked

idi527

idi527

Maybe Plug.Test — Plug v1.20.2

setup %{conn: conn} do
  {:ok, conn: Plug.Test.init_test_session(conn, user_id: 666)}
end

I wouldn’t change assigns manually, instead I’d pipe the requests through the actual plug pipelines with the session set via Plug.Test.init_test_session/2 to get the expected conn with all relevant assigns set.

LostKobrakai

LostKobrakai

Either what @idi527 said: Use the session and your real plugs / pipelines.
Or simply not reuse the conn for places where you’re e.g. testing a single plug.

result = MyPlug.call(conn, opts)
assert …

result = MyPlug.call(conn, opts_2)
assert …
LostKobrakai

LostKobrakai

That’s just not true. If you’re not using session based authentication you’d need to log in new for each request. The only data which survives between requests is the stuff you put into the session. Anything else is discarded at the end of a request. That’s true for the request you do with a browser in dev mode and exactly what recycle mimics for tests.

Last Post!

chensan

chensan

After reading the source code, I’d like to share what I learned so far.

When we run get(conn, Routes.site_path(conn, :show, site)), a new conn was created, and cookies and some request headers (~w(accept authorization)) from old conn are copied to new conn - that’s what Phoenix recycling does for us, it emulates the behavior of browser. But conn.assigns is not copied, results in a nil value of current_user.

The reason post(conn, Routes.session_path(conn, :create), session: %{email: user.email, password: "123456"} ) works is because Auth module selects another path:

  def call(conn, _opts) do
    user_id = get_session(conn, :user_id)

    cond do
      conn.assigns[:current_user] -> # <- the way programming phoenix book suggests
        conn

      user = user_id && Accounts.get_user(user_id) -> # <- post Routes.session_path just selects this way.
        assign(conn, :current_user, user)

      true ->
        assign(conn, :current_user, nil)
    end
  end

Thanks all for your kind help :slight_smile:

Where Next?

Popular in Questions Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36820 110
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31586 112
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42716 114
New

We're in Beta

About us Mission Statement