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.
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 <firstname.lastname@example.org> 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
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 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.
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
docker-compose up -d web
docker-compose run web mix deps.get
docker-compose run web mix compile
create the database:
docker-compose run web mix ecto.create
and migrate the database:
docker-compose run web mix ecto.migrate
Finally, let’s go ahead restart the
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 -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 test
If you want to test a specific file, you can just override the command:
docker-compose run 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