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:
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:
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.1pipelines:
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
.
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:
- databasedefinitions:
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 localhost
from 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.
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: