Automating Elixir Tests —Continuous Integration with Bitbucket Pipelines

Deploying a new version of an application can be a scary. When you’re putting a lot of work into crafting beautiful code and building great features, things can get messy and things can break. So once you’re finally prepared to push that deploy button, there’s always an unnerving uncertainty: Is it still going to work?

Continuous Integration to the Rescue!

Unit tests can give us peace of mind (at least some): Given sufficient code coverage, we can be relatively sure that a newly added feature hasn’t negatively affected other parts of our application when all tests are still passing.

If you’re anything like me, you probably don’t always manage to keep your local project directory in a “clean” state: There might always be some extra code lying around that is neither meant to be committed to your Git repository nor supposed to become part of any deployment. This means that even if your tests run fine locally, the code in your repository might still not work.

One approach to get around this problem is called Continuous Integration. Even if you’re not yet familiar with the term, you have probably seen its fruits in the form of pretty green badges in open source repositories all over GitHub and other sites:

Ecto is showing badges from not one but three CI tools: Travis CI, Inch CI and Ebert.

Even though Continuous Integration (or CI for short) might sound fancy and intimidating, it conveys a really simple concept: Frequently commit your code to a central repository and have it tested and built automatically.

CI from Your Git Repository

There are many great CI tools around — among the most popular are probably Travis CI (starting at $69/month, free for open source projects) and Circle CI (free small plan, paid plans starting at $50/month)

I wanted something not quite as expensive and — most of all — I didn’t feel like creating yet another account on yet another SaaS site. Fortunately, I am using Bitbucket for all my Git repositories and they have recently rolled out their own CI tool called Pipelines.

If you’re already using Bitbucket, all you need to do is enable Pipelines in the settings of your repository:

You can enable Pipelines in the settings of your Bitbucket repository

The next step is creating the bitbucket-pipelines.yml configuration file which tells Pipelines what to do when building/testing your project.

A Basic Test Setup

The basic configuration for an Elixir hello world application looks like this:

image: bitwalker/alpine-elixir:1.5.1
pipelines:
default:
- step:
script:
- mix test

Since Pipelines is built around Docker, first we define a Docker image which will be used to run everything in our pipeline. For most purposes, bitwalker’s alpine-elixir is a great choice because it’s really tiny and already comes with all the tools needed to build and test Elixir applications.

In the script section of the configuration file, we can define the commands needed to run this test. Since we don’t have any dependencies, we only need to call mix test.

All tests ran successfully!

I have created a little repository with all the necessary files here: https://bitbucket.org/pentacent/bitbucket-pipelines-test

Adding Ecto to the Mix

A typical real-world application usually involves other services such as a database. Fortunately it’s very easy to add additional Docker images to Pipelines. The following example uses the official Postgres image but MariaDB and MySQL are also available on Docker Hub and their configuration is very similar.

I have created an ecto branch of the initial repo which you can browse here: https://bitbucket.org/pentacent/bitbucket-pipelines-test/src/?at=ecto

Other than the usual modifications to our application (i. e. adding Ecto and postgrex to the list of dependencies, starting the ecto application and adding some migrations), we now have to update the Pipelines configuration.

The bitbucket-pipelines.yml configuration now looks like this (changes highlighted in bold print):

image: bitwalker/alpine-elixir:1.5.5
pipelines:
default:
- step:
script:
- MIX_ENV=test mix deps.get
- MIX_ENV=test mix ecto.create
- MIX_ENV=test mix ecto.migrate

- mix test
services:
- database
definitions:
services:
database:
image: postgres:9.6
environment:
POSTGRES_PASSWORD: pipelines-test

Before we do any actual testing, we pull required dependencies and execute all necessary Ecto migrations. Since we want to do this in order to test our application, everything is prefixed with MIX_ENV=test.

In the definitions section, we define a service called database to run the Docker image postgres:9.6 with the environment variable POSTGRES_PASSWORD set to pipelines-test (which automatically configures the database password to be just that).

Service images can be accessed directly via localhostfrom our main pipeline image. They can be very useful if you need to test your application against a variety of external services. You could, for example, also throw in a Minio image in order to test your application’s S3 capabilities.

Since the password mentioned above will only ever be used for testing within Pipelines, we don’t need to worry about keeping it outside our Git repository. Pipelines also supports “secret” environment variables which you could use to configure test credentials dynamically — but for simplicity’s sake, we’ll go with a static password in this example.

Finally, we configure our application to use the database we’ve now set up in config.exs(or, in a real-world application, probably your test.exs config file):

config :bitbucket_pipelines_test, BitbucketPipelinesTest.Repo,
adapter: Ecto.Adapters.Postgres,
pool: Ecto.Adapters.SQL.Sandbox,
url: "postgres://postgres:pipelines-test@localhost/test-db"

Once we git push the changes to the Bitbucket repository, the new pipeline will run automatically.

Lo and behold! The database tests are working.

That was easy!

There you have it! Continuous Integration with Elixir and you didn’t even have to sign up to a third-party service (provided you were using Bitbucket already, of course). Pipelines are free for up to 50 minutes a month. Additional build minutes are $10 for 1,000 additional minutes.


This article is part of an ongoing series about developing and deploying Elixir applications to production. Some of the next articles will cover developing with GenStage and Continuous Delivery (CD) for Elixir applications deployed to CloudFoundry PaaS.

In this series I am sharing my experiences when developing and deploying DBLSQD, a software release and update server written in Elixir. Feel free to check it out, there is a 60-day no-strings-attached free demo: