Skip to content

UrbanOS-Public/divo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hex.pm Version

Getting Started

Easily run Elixir integration tests with docker-compose. Provide Divo with docker-compose configuration, add use Divo to your integration tests, and run with mix test.integration.

Installation

The package can be installed by adding divo to your list of dependencies in mix.exs:

def deps() do
  [
    {:divo, "~> 2.0.0", only: [:dev, :integration]}
  ]
end

The docs can be found at https://hexdocs.pm/divo. New versions are published with actions upon github release.

Configuration

Docker

Define services in your mix configuration file to define the dockerized service(s) you want to run as a dependency of your Elixir app. Define divo config in one of the following three ways:

Method 1 - Compose file

In your config, include the path to the yaml or json-formatted compose file

#config/config.exs
config :myapp,
  divo: "test/support/docker-compose.yaml,
  divo_wait: [dwell: 700, max_tries: 50]
#test/support/docker-compose.yaml
version: '3'
services:
  redis:
    image: redis
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "PING"]
      interval: 5s
      timeout: 10s
      retries: 3

Method 2 - Pre-existing module

In your mix file, include the additional dependency

#mix.exs
def deps() do
  [
    {:divo, "~> 2.0.0", only: [:dev, :integration]},
    {:divo_redis, "~> 1.0.0", only: [:dev, :integration]}
  ]

And in your config, include the imported dependency module(s) as a list of tuples along with any environment variables the stack takes as a keyword list

#config/config.exs
config :myapp,
  divo: [
    {DivoRedis, [initial_key: "myapp:secret"]}
  ],
  divo_wait: [dwell: 700, max_tries: 50]
Known Modules:

Method 3 - Elixir map

#config/config.exs
config :myapp,
  divo: %{
    version: "3",
    services: %{
      redis: %{
        image: "redis:latest",
        ports: [
          "6379:6379"
        ],
        healthcheck: %{
          test: ["CMD", "redis-cli", "PING"],
          interval: "5s",
          timeout: "10s",
          retries: 3
        }
      }
    }
  },
  divo_wait: [dwell: 700, max_tries: 50]

Split Unit and Integration Tests

You will need to move any existing unit tests into a sub directory. We recommend the following structure:

myapp
  └── test
      ├── integration
      │   ├── myapp_test.exs
      │   └── test_helper.exs
      └── unit
          ├── module_a_test.exs
          ├── module_b_test.exs
          └── test_helper.exs

NOTE: test_helper.exs must be included in the root of both integration and unit tests.

Test Paths

Add this to your mix.exs to ensure that your unit and integration tests run in isolation from each other.

#mix.exs

def project do
  [
    # ...
    test_paths: test_paths(Mix.env())
  ]
end

# ...

defp test_paths(:integration), do: ["test/integration"]
defp test_paths(_), do: ["test/unit"]

Use Divo in an Integration Test

In each integration module add:

use Divo

Divo will then take care of running docker-compose up before running your tests and then run docker-compose down after they've completed.

NOTE: Divo will start and stop docker-compose for every integration test module, unless the "DIVO_DOWN" environment variable is set to "DISABLED".

Example Integration Test Module using redix:

defmodule MyAppTest do
  use ExUnit.Case
  use Divo

  test "persisting and reading from redis" do
    {:ok, conn} = Redix.start_link(host: "localhost", port: 6379)
    Redix.command(conn, ["SET", "mykey", "foo"])
    {:ok, result} = Redix.command(conn, ["GET", "mykey"])
    assert result === "foo"
  end
end

Use Divo for the entire test suite

In your integration or test suite's test_helper.exs file add Divo.Suite.start() before ExUnit.start() Example:

Divo.Suite.start()
...
ExUnit.start()

This will make Divo stand up dockers that last the entire run of the test suite (or just a few modules or tests if you specified them in your mix test.integration command). It will wire itself up to tear down the dockers if in cases where the tests fail to compile.

Ideally, you will want to NOT have use Divo in your tests. However, if you leave use Divo in for all of the tests, and still add the start to your test_helper.exs the tests will still run as expected, with an additional docker start and stop wrapped around the whole run.

The Divo.Suite.start function takes all of the options that use Divo does plus a few extras for controlling where the final docker cleanup occurs:

  • auto_cleanup? - whether or not to cleanup dockers on program exit. Defaults to true

Whether or not you choose to let it cleanup after itself, Divo.Suite.start will return a zero-arity cleanup hook that you can call when you want to explicitly cleanup the dockers.

Divo.Suite.start()
|> on_exit()

Running Integration Tests

Integration tests are executed by running:

mix test.integration

License

Released under Apache 2 license.