---
title: Testing HTML in Phoenix Controllers
teaser: Learn how to use ExMachina and Phoenix's ConnTest for robust controller tests.
tags: phoenix,elixir,testing
author: Paul Smith
published_on: 2015-12-17
---

Phoenix has a nice [set of helpers] for testing HTML responses. The default
Phoenix generators offer a great way to get a quick start on testing HTML, but
this guide hopes to dive in a bit further.

In this post we'll go over:

* Using the Phoenix helpers to test your controllers and their responses.
* Some common errors, what they mean, and how to fix them.
* How to use [ExMachina] (a [Factory Bot] like library) to generate test data.
* How to use regular functions and the Elixir `|>` to customize test data
  generated by ExMachina.

[set of helpers]: http://hexdocs.pm/phoenix/Phoenix.ConnTest.html
[ExMachina]: https://github.com/thoughtbot/ex_machina
[Factory Bot]: https://github.com/thoughtbot/factory_bot

## Setup

Follow the [Phoenix installation instructions] and then start a new project with
`mix phoenix.new my_blog`. Once all the dependencies are installed and you've
created your database with `mix ecto.create`, we can get started.

[Phoenix installation instructions]: http://www.phoenixframework.org/docs/installation

## Testing the list of blog posts

Let's create a file at `test/controllers/post_controller_test.exs`

```elixir
defmodule MyBlog.PostControllerTest do
  use MyBlog.ConnCase

  setup do
    {:ok, conn: Phoenix.ConnTest.conn()}
  end

  test "lists all blog posts", %{conn: conn} do
    posts = create_pair(:post)

    conn = get conn, post_path(conn, :index)

    for post <- posts do
      assert html_response(conn, 200) =~ post.title
    end
  end
end
```

In the `setup` block we are generating a conn to be used. Note that when Phoenix
1.1 is released this [will be done automatically] by `MyBlog.ConnCase`.

The second part of the tuple returned in the `setup` block is available as the
second argument to your test. We pattern match against it to make the `conn`
available to our test.

[will be done automatically]: https://github.com/phoenixframework/phoenix/commit/431caef8a27fcad5485c7f2b20ca8e2cee04c17d

We are calling the `ExMachina` function called [`create_pair/3`] that will
insert 2 posts. We could have used `build_pair/3` if we didn't need to save to
the database, but in this case we need the posts saved.

[`create_pair/3`]: http://hexdocs.pm/ex_machina/ExMachina.html#create_pair/3

Next we make a request to our controller and then check that the response has
the titles of each of our blog posts.

The `html_response` helper makes sure that the response was successful (status
code 200) and returns the HTML body. Using `=~` checks that whatever is on the
right hand side (`post.title`) is found somewhere in the string on the left hand
side (the HTML body).

Now let's try to run the tests with `mix test`. You should get an error like
this:

    ** (CompileError) test/controllers/post_controller_test.exs:5: function create_pair/1 undefined

## Setting up ExMachina

To fix that, let's add ExMachina and add our factory.

Add `{:ex_machina, "~> 0.5.0"}` to your `mix.exs` dependencies. It will look
something like this:

```elixir
defp deps do
  [{:ex_machina, "~> 0.5.0"},
   {:phoenix, "~> 1.0.3"},
   # Other dependencies...
  ]
end
```

Then add `:ex_machina` to your list of applications. In a default Phoenix app
this will look something like this:

```elixir
def application do
  [mod: {MyBlog, []},
   applications: [:ex_machina, :phoenix, :phoenix_html, :cowboy, :logger,
                  :phoenix_ecto, :postgrex]]
end
```

Now let's create our factory for generating test data. We'll do this in
`lib/my_blog/factory.ex`. Be sure to make the extension `.ex` and not `.exs` or
it will not be compiled and you will get errors. See the [ExMachina README] for
the latest.

[ExMachina README]: https://github.com/thoughtbot/ex_machina

```elixir
defmodule MyBlog.Factory do
  use ExMachina.Ecto, repo: MyBlog.Repo

  def factory(:post) do
    %MyBlog.Post{
      title: sequence(:title, &"My Post #{&1}"),
      body: "This is my post about something",
      author: "Me!"
    }
  end
end
```

This is setting up a factory called `:post` that is built using the
`MyBlog.Post` struct. The body and author are static, but the title uses the
`sequence/2` to ensure that the title is always unique. Whenever a post is built
or created `n` will be incremented by 1. The `&("My Post #{&1}")` syntax is a
shorthand for `fn(n) -> "My Post #{n}" end`. You can use whichever one you are
most comfortable with.

Let's add our factory to `MyBlog.ConnCase` so that our factory functions are
available in all of our controller tests.

In `test/support/conn_case.ex` add `import MyBlog.Factory` in the `using` block.

```elixir
using do
  quote do
    # ...other code automatically generated by Phoenix when you start a project
    import MyBlog.Factory
  end
end
```

## Adding the Post model

If we run `mix test` we will now get:

    == Compilation error on file test/support/factory.ex ==
    ** (CompileError) test/support/factory.ex:5: MyBlog.Post.__struct__/0 is
    undefined, cannot expand struct MyBlog.Post

This means that we have not set up a struct called `MyBlog.Post`. Let's create a
model and migration to add it. Run:

<kbd>
mix phoenix.gen.model Post posts title:string body:text author:string
<br />
mix ecto.migrate
</kbd>

## Adding routes and controller action

Running `mix test` will now show us:

    ** (CompileError) test/controllers/post_controller_test.exs:11: function posts_path/2 undefined

Let's go ahead and add our route and controller action now.

In `web/router.ex` add your route to the default browser scope

```elixir
scope "/", MyBlog do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/posts", PostController, only: [:index]
end
```

Then create `web/controllers/post_controller.ex`

```elixir
defmodule MyBlog.PostController do
  use MyBlog.Web, :controller

  def index(conn, _params) do
  end
end
```

## Get and display posts

Let's finish off the controller and make sure we are rendering our posts.

```elixir
defmodule MyBlog.PostController do
  use MyBlog.Web, :controller

  alias MyBlog.Post

  def index(conn, _params) do
    # MyBlog.Repo is aliased for you when you `use MyBlog.Web, :controller`
    posts = Repo.all(Post)
    render(conn, "index.html", posts: posts)
  end
end
```

If we run `mix test` we'll see something like this:

    undefined function: MyBlog.PostView.render/2 (module MyBlog.PostView is not available)

That means we need to add our view and template. Let's add a view at
`web/views/post_view.ex`

```elixir
defmodule MyBlog.PostView do
  use MyBlog.Web, :view
end
```

And a template at `web/templates/post/index.html.eex`

```elixir
<%= for post <- @posts do %>
  <%= post.title %>
<% end %>
```

Now when you run `mix test` everything should pass smoothly.

## Using function composition for more robust tests

Let's say that our post can be tagged. A post can be tagged with multiple tags.
We'll start by updating our factory. We'll assume our models have already been
created and jump to updating our tests.

```elixir
def factory(:tag) do
  %MyBlog.Tag{
    name: sequence(:tag_name, fn(n) -> "Tag #{n}" end)
  }
end

def factory(:post_tag) do
  %MyBlog.PostTag{
    post: build(:post)
    tag: build(:tag)
  }
end
```

We're going to use another sequence so that when we build or create a :tag, the
name is always unique. When building a :post_tag we will build a post and a tag
by default. This can be overridden just like any other attribute. We'll see how
to do that in a second.

Let's add a test:

```elixir
test "list of post shows the post's tags", %{conn: conn} do
  tag_name = "elixir"
  create(:post)
  tag = create(:tag, name: tag_name)
  create(:post_tag, tag: tag, post: post)

  conn = get conn, post_path(conn, :index)

  assert html_response(conn, 200) =~ tag_name
end
```

This test will work, but the problem is that the test is brittle and not very
expressive. If someone needs to create a tagged post in another test they will
have to remember to do this. If you change how you create posts, you will then
need to change this in every test that uses it.

Instead let's add a function to our factory in `lib/my_blog/factory.ex` to make
this more clear.

```elixir
def tagged_with(post, tag_name) do
  tag = create(:tag, name: tag_name)
  create(:post_tag, post: post, tag: tag)
  post
end
```

Now our test will look like this

```elixir
test "list of post shows the post's tags", %{conn: conn} do
  tag_name = "elixir"
  post = create(:post) |> tagged_with(tag_name)

  conn = get conn, post_path(conn, :index)

  assert html_response(conn, 200) =~ tag_name
end
```

Now if you need to create a tagged post in another test, it is much easier to do
so. This is also nice because if we change how we tag posts we can just update
the `tagged_with/2` function instead of changing every test.

Using regular functions with ExMachina can make your code easier to read, and
easier to change in the future.

This is a small taste of what you can do with ExMachina and the Phoenix tests.
Be sure to check out the [Phoenix.ConnTest docs] and the [ExMachina docs] for
more examples.

[Phoenix.ConnTest docs]: http://hexdocs.pm/phoenix/Phoenix.ConnTest.html
[ExMachina docs]: http://hexdocs.pm/ex_machina/README.html
