A Few Caveats to Running Elixir Tests in Containers and CI

noelworden profile image Noel Worden ・3 min read

As a fitting follow up to last week's post 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:

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:

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:

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. I found a post 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:

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:

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 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:

Adding an environmental variable looks like this:

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

But that one-liner can be refactored even further. Theres also a System.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:

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, linked to the point where this was work was merged in.

This post is part of an ongoing This Week I Learned series. I welcome any critique, feedback, or suggestions in the comments.

Posted on by:

noelworden profile

Noel Worden


Software Engineer in Boulder, CO - Writing code and getting strategically lost in the mountains


Editor guide