Excontainers
Throwaway test containers for Elixir/Erlang applications. Heavily inspired by Testcontainers.
This package is still in the early stages of development. You are encouraged to give it a try, but you should not regard it as ready for critical real world scenarios.
Installation
The package can be installed by adding excontainers to your list of dependencies in mix.exs:
def deps do
[
{:excontainers, "~> 0.2.0", only: [:dev, :test]},
]
endDocumentation can be found at https://hexdocs.pm/excontainers.
Usage
ExUnit
Create a throwaway container (in this case Redis) within a ExUnit test:
defmodule Excontainers.RedisContainerTest do
use ExUnit.Case, async: true
import Excontainers.ExUnit
alias Excontainers.RedisContainer
container(:redis, RedisContainer.new())
test "provides a ready-to-use redis container", %{redis: redis} do
{:ok, conn} = Redix.start_link(RedisContainer.connection_url(redis))
assert Redix.command!(conn, ["PING"]) == "PONG"
end
end
Containers declared using the container helper are created for each test.
Alternatively, you can use shared_container to declare containers that are created once per each module and shared among its tests.
To create a container for a specific test only, use the run_container macro as follows:
test "my test" do
{:ok, redis} = run_container(RedisContainer.new())
connection_url = RedisContainer.connection_url(redis)
# ...
endcontainer, shared_container and run_container will take care of cleaning up the containers once they are no longer needed.
Direct usage
If you want to use Excontainers outside of your Exunit tests,
or if you'd like to have direct control over the lifecycle of your containers,
you can use the Excontainers.Container agent:
{:ok, pid} = Container.start_link(@sample_container_config)
{:ok, container_id} = Container.start(pid)
:ok = Container.stop(pid)Containers
The following containers are currently provided pre-configured:
Excontainers.MySqlContainerExcontainers.PostgresContainerExcontainers.RedisContainer
Please open an issue if you'd like to see new ones.
Custom containers
Excontainers can run any container that docker can.
Custom container configurations can be built via Docker.Container.new.
For example:
custom_container_config = Docker.Container.new(
"alpine"
cmd: ~w(echo hello world!),
labels: %{"test-label-key" => "test-label-value"},
privileged: false,
environment: %{"SOME_KEY" => "SOME_VAL"},
exposed_ports: [8080],
bind_mounts: [Docker.BindMount.new("host/src", "container/dest/", "ro")],
wait_strategy: Docker.CommandWaitStrategy.new(["./command/to/check/if/container/is/ready.sh"])
)A builder-like API to customize container configuration is also provided:
alias Docker.Container
custom_container_config =
Container.new("alpine", cmd: ~w("echo hello world!"), privileged: false)
|> Container.with_environment("SOME_KEY", "SOME_VAL")
|> Container.with_exposed_port(8080)
|> Container.with_bind_mount("host/src", "container/dest", "ro")Resources Reaping
Under normal circumstances, Excontainers removes the containers it spawned after they are no longer useful (i.e., during the teardown phase of tests). However, it may fail to do so when tests are interrupted abruptly, preventing ExUnit from running the necessary callbacks.
Excontainers provides a Resources Reaper that makes sure containers are removed when they are no longer useful. It runs in its own docker container, so it is not affected by crashes or problems with the tests suite.
To enable the Resources Reaper, simply spawn it before you run your tests, e.g., by adding this to your tests_helper.exs:
Excontainers.ResourcesReaper.start_link()
Containers managed via the container and shared_container helpers for ExUnit are automatically registered to the Resources Reaper.
When controlling the lifecycle of containers manually, containers can be registered to the Resources Reaper like this:
Excontainers.ResourcesReaper.register({"id", my_container_id}){"id", my_container_id} is a filter for docker resources that works on the id of the container.
Other attributes (e.g., labels) could also be used.
Development
Testing
Tests require a machine with a docker daemon listening on the default unix socket /var/run/docker.sock and the cli docker client installed.
Run tests with
mix testTODO
- Mock interaction with docker in non-e2e tests for Excontainers?
- Add logs wait strategy
- Add TCP connection available wait strategy, and use it in tests that rely on echo http server, as sometimes it fails for not being initialized in time
- Add more pre-configured images (localstack, rabbit, ...)