A Few Caveats to Running Elixir Tests in Containers and CI
Tags: docker, elixir, devops, thisweekilearned
Published: 2020-09-17 14:18:08

As a fitting follow up to last week's [post](https://dev.to/noelworden/docker-connecting-an-elixir-phoenix-app-container-and-database-container-2n0a) dealing with proper database configuration and Docker containers, this week I ran into a related snag while trying to get my tests to run in the app container. When I ran the `mix test` command through `docker-compose`:

`docker-compose exec web mix test`

It failed with the following error:

{% gist https://gist.github.com/noelworden/bfb6c9c21a465d7f6c6c2eeae860dddf %}

Although this wasn't the _exact_ same error from my previous experience, it's in a similar vein. I checked out the `test.exs` configuration, and low and behold, in the Repo configuration I found:

{% gist https://gist.github.com/noelworden/552d4f102091bd61a192949ac4d59784 %}

I updated `hostname` from `"localhost"` to `"db"` -the name of the database container- and re-ran:

`docker-compose exec web mix test`

It successfully compiled, ran the tests, and everything was good to go, or so I thought. After creation of the pull request the CI spun up a new check... and it failed. To my surprise, it was failing at the same step and showing the same error as I discussed in last weeks post, only this time instead of the error happening on my local machine, it was happening on the CI build:

{% gist https://gist.github.com/noelworden/5ddff826f874a31d75910c8ca494ee7e %}

After changing the `test.exs` configuration and pushing that up to the pull request to confirm, I concluded that the CI workflow needed the testing database `hostname` to be `"localhost"` while it _also_ needed to be `"db"` for the tests to run in the app container.

The solution I found for this utilized [`System.get_env/1`](https://hexdocs.pm/elixir/System.html#get_env/2). I found a [post](https://github.com/actions/setup-elixir#authenticating-with-postgres-in-phoenix) that shows how to set configurations based on environment variables. When the app is being run in the CI workflow, there is a `GITHUB_ACTION` environmental variable set, so a conditional can be built around that:

{% gist https://gist.github.com/noelworden/f2fe933319c5fda0070965b6e055b1f3 %}

Placing that conditional under the initial configuration will override the line `hostname: "db"` when the app is running in a CI workflow. The entire setup looks like this:

{% gist https://gist.github.com/noelworden/9e4a72e8c89c357b8a20bb64481ebc97 %}

With that conditional set, the tests successfully run in the local app container using `"db"` _and_ in the CI workflow using `"localhost"`.

But there's more, a potential for refactoring! While researching and digging deeper into this, I came across a [post](https://dev.to/jonoyeong/how-to-setup-ci-with-phoenix-github-actions-4k68) that utlized environment variables in a slightly different way, which I actually preferred and refactored my work to emulate. He suggested setting an environmental variable in the CI workflow file itself, then injecting that variable as a conditional in the original database configuration. My original `mix test` command for the CI workflow was this:

{% gist https://gist.github.com/noelworden/e3c57cc29114a783c28f83fe078f373a %}

Adding an environmental variable looks like this:

{% gist https://gist.github.com/noelworden/bace340e7f19db85ed6241b479decd62 %}

Now, in `test.exs`, instead of that entire conditional _under_ the original configuration, the `hostname` field can be set conditionally as a one-liner:

{% gist https://gist.github.com/noelworden/b9a356232f9916df075b85936093350a %}

**But** that one-liner can be refactored _even further_. Theres also a [`System.get_env/2`](https://hexdocs.pm/elixir/System.html#get_env/2) that allows for two arguments to be passed, the first is the environmental variable, and the second is a default value if that environmental variable cannot be found. So, instead of using `||` to set the `hostname` to `"db"` if `System.get_env("DB_HOSTNAME")` returns `nil`, it can be all be done in the single function call:

{% gist https://gist.github.com/noelworden/ce1c530f23f0f528c5ecd7d555827160 %}

With that refactor both testing environments run error free, and the codebase is just a touch cleaner. 

If you'd like to see the complete files, feel free to explore my public [repo](https://github.com/noelworden/atlas/tree/e33a6f3e2b9f9eda2b2cc6f91a16e55a0367d593), linked to the point where this was work was merged in. 


------

This post is part of an ongoing *This Week I Learned* [series](https://dev.to/noelworden/beginning-of-a-blog-series-5aj3). I welcome any critique, feedback, or suggestions in the comments.