Running Your Phoenix Tests Using Docker

Written by: Jason Kriss

5 min read

UPDATE: With January 1st, 2017 we rebranded our hosted CI Platform for Docker from “Jet” to what is now known as “Codeship Pro”. Please be aware that the name “Jet” is only being used four our local development CLI tool. The Jet CLI is used to locally debug and test builds for Codeship Pro, as well as to assist with several important tasks like encrypting secure credentials.

One of the many benefits of using Docker is that it can make testing your applications much easier. Want to test out your Phoenix app on the newest version of Elixir? Easy -- just change one line in the Dockerfile.

In this post, we'll walk through setting up a Phoenix project with Docker, which will allow us to run our application's tests using Docker. We'll be working with just a basic, scaffolded Phoenix app, as the specifics of the app are not important for the purposes of this post. Extrapolating out to a real-world app should be straightforward, so we'll be focusing on the Docker side of things.

This post assumes a basic familiarity with Phoenix as well as Docker. You will need to have Docker running on your development machine, but that's it. Everything else will be taken care of within the context of Docker.

Dockerizing Phoenix

We'll start from just an empty directory here. Go ahead and create the directory mkdir phoenix_docker && cd phoenix_docker.

Now add a basic Dockerfile with just the following contents:

FROM elixir:onbuild
MAINTAINER Your Name <your.email@example.com>
RUN mix local.hex --force
RUN mix archive.install --force https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
WORKDIR /app

This Dockerfile builds off of the official Elixir base image. We then install the Hex package manager and Phoenix archive locally before setting the working directory to /app.

Next let's go ahead and set up Docker Compose. Add a docker-compose.yml file with the following contents:

web:
  build: .
  ports:
    - "4000:4000"
  command: mix phoenix.server
  environment:
    - MIX_ENV=dev
    - PORT=4000
  volumes:
    - .:/app

This will build from the our Dockerfile and run the command mix phoenix.server by default. However, we can also specify a different command to run in the container. This is exactly what we will do in order to bootstrap our Phoenix app:

docker-compose run --rm web mix phoenix.new . --app phoenix_docker --no-brunch

This will create a new Phoenix application in the working directory. Furthermore, since we specified a volume in the docker-compose.yml, this generated app will persist to our local phoenix_docker directory. That is, we were able to generate our Phoenix application without needing to download and install anything other than Docker on our local machine. This is a great start, but in order to actually run the application, we are going to need a database.

Adding Postgres

Phoenix works with PostgreSQL by default, so let's get that up and running. Docker Compose makes it super easy to set up Postgres and link our application to it.

First, update the docker-compose.yml to include the db service:

web:
  build: .
  ports:
    - "4000:4000"
  command: mix phoenix.server
  environment:
    - MIX_ENV=dev
    - PORT=4000
  volumes:
    - .:/app
  links:
    - db
db:
  image: postgres
  environment:
    - POSTGRES_USER=postgres
    - POSTGRES_PASSWORD=postgres
    - POSTGRES_HOST=db

Notice that we added the db link in our web service. This will make Postgres available to our app and will also expose the POSTGRES_* environment variables within our web service. Therefore, we can now update our dev Phoenix database configuration in config/dev.exs with the following:

config :phoenix_docker, PhoenixDocker.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: System.get_env("DB_ENV_POSTGRES_USER"),
  password: System.get_env("DB_ENV_POSTGRES_PASSWORD"),
  hostname: System.get_env("DB_ENV_POSTGRES_HOST"),
  database: "phoenix_docker_dev",
  pool_size: 10

Now we can start up the web service:

docker-compose up -d web

install dependencies:

docker-compose exec web mix deps.get

compile:

docker-compose exec web mix compile

create the database:

docker-compose exec web mix ecto.create

and migrate the database:

docker-compose exec web mix ecto.migrate

Finally, let's go ahead restart the web service:

docker-compose restart web

You should now be able access the running application! Let's move on to testing the application with Docker.

Running Your Tests

Now that we have the basic Docker setup for our Phoenix application, running tests is easy. The quickest way to get started is to just run tests the same way you would locally:

docker-compose run --rm -e "MIX_ENV=test" web mix test

This works perfectly fine but isn't terribly extensible. Another option is to set up a dedicated test service which we will do here. Update docker-compose.yml to the following:

web:
  build: .
  ports:
    - "4000:4000"
  command: mix phoenix.server
  environment:
    - MIX_ENV=dev
    - PORT=4000
  volumes:
    - .:/app
  links:
    - db
db:
  image: postgres
  environment:
    - POSTGRES_USER=postgres
    - POSTGRES_PASSWORD=postgres
    - POSTGRES_HOST=db
test:
  image: phoenixdocker_web
  command: mix test
  environment:
    - MIX_ENV=test
  volumes_from:
    - web
  links:
    - db

Then update the test Phoenix database configuration in config/test.exs to be:

config :phoenix_docker, PhoenixDocker.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: System.get_env("DB_ENV_POSTGRES_USER"),
  password: System.get_env("DB_ENV_POSTGRES_PASSWORD"),
  hostname: System.get_env("DB_ENV_POSTGRES_HOST"),
  database: "phoenix_docker_test",
  pool: Ecto.Adapters.SQL.Sandbox

With that, we can now run our tests by simply running:

docker-compose run --rm test

If you want to test a specific file, you can just override the command:

docker-compose run --rm test mix test test/controllers/page_controller_test.exs

Running our tests in this way has the added benefit that if we need some test-only dependencies, we don't need to add them to our web service. This can be especially useful for something like browser testing.

Where to Go From Here

So far, we've set up a Phoenix application with Docker that uses a Postgres database. We use Docker to run the tests for this application in their own service. The application in this walkthrough is very simple, but extending this process to a more complicated app should require very little extra work.

Now that we are using Docker to run our Phoenix tests, extending our test suite to include other types of tests is very easy. For example, we can run acceptance tests using something like Hound together with PhantomJS. All we need to do is install PhantomJS in our Dockerfile (perhaps creating a test-specific Dockerfile) and add Hound to our Phoenix dependencies in mix.exs.

Another natural next step is to utilize Codeship's Docker-based CI tool, Jet. With the start we have here, it's very easy to get up and running with Jet. You can read more about it here and here.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.