Testing Authenticated Routes in Phoenix

August 4, 2021

This will be a quick one, but it was tough for me to find.

Just some background: I used phx_gen_auth for authentication in my app. This little tidbit is going to play an important role in my "no duh" moment.

After using phx.gen.html or phx.gen.live to do some heavy lifting, Phoenix is nice enough to write out some tests for me as well.

Only problem is when I require an authenticated user for a certain route or resource. My nicely passing out-of-the-box tests no longer pass.

Well, jumping into the code, I just started by fixing one test at a time. I looked in the user_session_controller_test.exs file generated from running phx.gen.auth and found how they created the user:

import MyAwesomeProject.AccountsFixtures

setup do
  %{user: user_fixture()}
end

Okay, great! That means I can easily run user_fixture() to create a user and pass it into each test.

I then looked how the user was logged in in the test:

test "redirects if already logged in", %{conn: conn, user: user} do
  conn = conn
  |> log_in_user(user)
  |> get(Routes.user_session_path(conn, :new))
  assert redirected_to(conn) == "/"
end

Okay, simple enough. There's nice conveniently named function called log_in_user/2 that takes conn and user as parameters. Nice. I can do that on each of my tests too. But wait, that's not very DRY at all. Well, let's not even pass in the user to each test when I can just pass in an updated conn after logging in the user with log_in_user/2.

defp log_in_user(%{conn: conn}) do
  %{conn: log_in_user(conn, user_fixture())}
end

...

describe "MyAwesomeTest" do
  setup [:log_in_user]
  ...

But wait, where did log_in_user/2 come from. I had just made an assumption that it came from my import of MyAwesomeProject.AccountsFixtures. It actually came from MyAwesomeProject.ConnCase. It's that phx.gen.auth magic at work again!

And you know what I found when I just looked around in conn_case.exs?

@doc """
Setup helper that registers and logs in users.

    setup :register_and_log_in_user

It stores an updated connection and a registered user in the
test context.
"""
def register_and_log_in_user(%{conn: conn}) do
  user = GrokTheGreeks.AccountsFixtures.user_fixture()
  %{conn: log_in_user(conn, user), user: user}
end

I've never been happier knowing I wasted my time writing throw-away code.

Subscribe

I'd like to send you a weekly recap of all the articles I write as well as my take on the latest news on web development with Vue, React, Elixir/Phoenix, and others. Just fill out your email and name below!


Prev: Add Authentication to Your AdonisJS Project
Next: Testing Authenticated Routes in AdonisJS