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.