---
title: Testing Elixir Plugs
teaser: Unit-test your Elixir Plugs with a few quick tips.
tags: web,elixir,testing,phoenix
author: Josh Clayton
published_on: 2016-05-11
---

On a [Phoenix] application I worked on recently, I decided to tackle a bug
where we weren't redirecting users to a sign-in page even though we were
expecting `conn.assigns` to have `current_user`. This was only happening in
a few different areas of the app. [Plugs] to the rescue.

[Plugs]: http://www.phoenixframework.org/v1.1.4/docs/understanding-plug
[Phoenix]: http://www.phoenixframework.org

## What are Plugs?

Per [the Plug GitHub page], a plug is a "specification for composable modules
between web applications."

[the Plug GitHub page]: https://github.com/elixir-lang/plug

Although some might compare Plugs to [Rack middleware], they operate on the
entire request and response lifecycle; in fact, Endpoints, Routers, and
Controllers within Phoenix are all Plugs internally.

[Rack middleware]: http://guides.rubyonrails.org/rails_on_rack.html

## Writing a Small Plug

Let's dig into a plug that has the responsibility described above. There
should be two paths through the plug:

1. The connection has a `current_user` and should continue
2. The connection does not have a `current_user` and should stop
   everything and redirect the user to the sign-in page

Let's create a new file in `web/plugs/require_login.ex`:

```elixir
defmodule MyApp.Plugs.RequireLogin do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _) do
    conn
  end
end
```

This is a minimal interface for a module plug. `call` is where we'll handle
letting the connection continue or halting execution; let's flesh out the
paths now:

```elixir
# web/plugs/require_login.ex

def call(conn, _) do
  if conn.assigns[:current_user] do
    # everything is good
  else
    # uh-oh - ask the user to sign in
  end
end
```

In our "happy path" when a `current_user` is set, we'll need to pass the
`conn` through so it can continue on its merry way.

When `conn.assigns[:current_user]` doesn't return a truthy value, we'll want
to redirect them to `"/sign_in"`, where we'll prompt them to sign in. Also note that
we're accessing `current_user` via `[]`; we do so instead of
`conn.assigns.current_user` because if `current_user` hasn't been assigned,
the application will raise a `KeyError`. Check out the Elixir documentation on
the [`Access` behaviour] if you want to learn more.

[`Access` behaviour]: http://elixir-lang.org/docs/stable/elixir/Access.html

```elixir
# web/plugs/require_login.ex

def call(conn, _) do
  if conn.assigns[:current_user] do
    conn
  else
    conn |> Phoenix.Controller.redirect(to: "/sign_in")
  end
end
```

Because of the nature of Plugs, we're letting [`Phoenix.Controller.redirect/2`] do
the heavy lifting for us.

[`Phoenix.Controller.redirect/2`]: https://hexdocs.pm/phoenix/Phoenix.Controller.html#redirect/2

## Halt!

If we used the code above, we'd begin to see problems. As mentioned
previously, because Plugs cover the entire lifecycle of the connection,
calling `Phoenix.Controller.redirect/2` by itself actually isn't enough; we
also need to call `Plug.Conn.halt/1` to stop further execution and immediately
process the redirect.

Let's refactor a bit before we move on:

```elixir
defmodule MyApp.Plugs.RequireLogin do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _) do
    if conn.assigns[:current_user] do
      conn
    else
      conn |> redirect_to_login
    end
  end

  defp redirect_to_login(conn) do
    conn |> Phoenix.Controller.redirect(to: "/sign_in") |> halt
  end
end
```

## Testing the Plug

Because we don't need to build up or manage complicated state for our
connection, and because our functions allow for known inputs and outputs,
testing the plug is fairly painless.

Let's start with a new test:

```elixir
# test/plugs/require_login_test.exs

defmodule MyApp.Plugs.RequireLoginTest do
  use MyApp.ConnCase

  test "user is redirected when current_user is not assigned" do
    # build a connection and run the plug

    assert redirected_to(conn) == "/sign_in"
  end

  test "user passes through when current_user is assigned" do
    # build a connection, assign current_user, and run the plug

    assert conn.status != 302
  end
end
```

We'll `use MyApp.ConnCase`, as that will give us access to various
functions to interact with a connection. To get the first test passing, we'll
need to generate a connection and then run our plug.

```elixir
# test/plugs/require_login_test.exs

test "user is redirected when current_user is not assigned" do
  conn = conn() |> MyApp.Plugs.RequireLogin.call(%{})

  assert redirected_to(conn) == "/sign_in"
end
```

We can generate a connection with [`Phoenix.ConnTest.conn/0`] and then pipe
that connection to our plug. This test should be green.

[`Phoenix.ConnTest.conn/0`]: https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#conn/0

For our second test, we'll need to introduce another step where we assign
`current_user`.

```elixir
# test/plugs/require_login_test.exs

test "user passes through when current_user is set" do
  conn =
    conn()
    |> assign(:current_user, %MyApp.User{})
    |> MyApp.Plugs.RequireLogin.call(%{})

  assert conn.status != 302
end
```

Whew. Finally, a bit of refactoring leaves us with:

```elixir
defmodule MyApp.Plugs.RequireLoginTest do
  use MyApp.ConnCase

  test "user is redirected when current_user is not set" do
    conn = conn() |> require_login

    assert redirected_to(conn) == "/sign_in"
  end

  test "user passes through when current_user is set" do
    conn = conn() |> authenticate |> require_login

    assert not_redirected?(conn)
  end

  defp require_login(conn) do
    conn |> MyApp.Plugs.RequireLogin.call(%{})
  end

  defp authenticate(conn) do
    conn |> assign(:current_user, %MyApp.User{})
  end

  defp not_redirected?(conn) do
    conn.status != 302
  end
end
```

Of note is the `not_redirected?/1` function here; the reason we assert that
the status is "not a `302`" is because `Phoenix.ConnTest.conn/0` doesn't
actually set a `status` and leaves the value as `nil`. `assert conn.status ==
nil` seemed less intuitive than asserting a redirect did not occur.

## Plugs and the Router

With the plug working and tested, using it in the router is almost
anticlimactic.

```elixir
# web/router.ex

defmodule MyApp.Router do
  use Phoenix.Router

  pipeline :browser do
    plug :accepts, ~w(html)

    # ...
  end

  # endpoints not requiring a logged in user
  scope "/", MyApp do
    pipe_through :browser

    # ... resources
  end

  # endpoints requiring a logged in user
  scope "/", MyApp do
    pipe_through [:browser, MyApp.Plugs.RequireLogin]

    # ... resources
  end
end
```

This introduces a new [`scope`] block where we pass a list to
[`Phoenix.Router.pipe_through/1`], and then declare all resources requiring
`current_user` within that block. These plugs are executed sequentially, so it
will first run through the `:browser` pipeline, and then through our plug.

[`scope`]: https://hexdocs.pm/phoenix/Phoenix.Router.html#scope/2
[`Phoenix.Router.pipe_through/1`]: https://hexdocs.pm/phoenix/Phoenix.Router.html#pipe_through/1

## Explore Other Plugs

I hope this was a helpful guide in authoring and unit-testing your own Elixir
plugs; testing plugs in isolation can be daunting if you've never done it
before. If you're looking for other inspiration, I encourage you to look at
the [tests written for Plug itself] to understand different approaches you can
take.

[tests written for Plug itself]: https://github.com/elixir-lang/plug/tree/master/test/plug
