Skip to main content

PyPI pyversions PyPI version GitHub release

pytest-docker-compose-v2

NOTE: This is a fork from the pytest-docker-compose project which at the time of writing hasn't been updated in 2 years -- I depend on this so I'm taking a crack at running a -v2 fork, but I'm definitely willing to re-integrate any changes back to the original project. Currently the changes are pretty basic and include a PR that introduces support for docker compose v2, some package updates, and more superficial updates to align with personal preferences. Though I am making an effort to run the existing test suite, I have not tested this extensively.

This package contains a pytest plugin for integrating Docker Compose into your automated integration tests.

Given a path to a docker-compose.yml file, it will automatically build the project at the start of the test run, bring the containers up before each test starts, and tear them down after each test ends.

Dependencies

Make sure you have Docker installed.

This plugin is automatically tested against the following software:

  • Python 3.10, 3.11, 3.12, 3.13 and 3.14
  • pytest 7, 8 and 9

NOTE: This plugin is not compatible with Python 2.

Installation

Install the plugin using pip:

pip install pytest-docker-compose-v2

Usage

The plugin is automatically enabled when installed. To disable it for specific test runs, use:

pytest -p no:docker_compose

To interact with Docker containers in your tests, use the following fixtures. These fixtures tell docker-compose to start all the services and then fetch the associated containers for use in a test:

function_scoped_container_getter

An object that fetches containers of the Docker python_on_whales.Container objects running during the test. The containers are fetched using function_scoped_container_getter.get('service_name') These containers each have an extra attribute called network_info added to them. This attribute has a list of pytest_docker_compose.NetworkInfo objects.

This information can be used to configure API clients and other objects that will connect to services exposed by the Docker containers in your tests.

NetworkInfo is a container with the following fields:

  • container_port: The port (and usually also protocol name) exposed internally to the container. You can use this value to find the correct port for your test, when the container exposes multiple ports.
  • hostname: The hostname (usually "localhost") to use when connecting to the service from the host.
  • host_port: The port number to use when connecting to the service from the host.

docker_project

The python_on_whales.DockerClient object that the containers are built from. This fixture is generally only used internally by the plugin.

Wider scoped fixtures

To use the following fixtures please read Use wider scoped fixtures

  • class_scoped_container_getter: Similar to function_scoped_container_getter just with a wider scope.
  • module_scoped_container_getter: Similar to function_scoped_container_getter just with a wider scope.
  • session_scoped_container_getter: Similar to function_scoped_container_getter just with a wider scope.

Waiting for Services to Come Online

The fixtures called [scope]_scoped_container_getter will wait until every container is up before handing control over to the test.

However, just because a container is up does not mean that the services running on it are ready to accept incoming requests yet!

The plugin can use Docker Compose's native --wait functionality with healthchecks defined in your docker-compose.yml. This lets Docker Compose handle waiting for services to be ready.

First, define healthchecks in your docker-compose.yml:

services:
  my_api_service:
    build: ./api
    ports:
      - "5000:5000"
    depends_on:
      my_db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s
  my_db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 10

Then opt in to waiting when you run pytest:

pytest --docker-compose-wait

You can also specify a timeout (in seconds):

pytest --docker-compose-wait --docker-compose-wait-timeout=120

Waiting remains opt-in for backward compatibility. It is planned to become the default in version 1.0.

Option 2: Python-based Wait Fixtures

If your tests need to wait for a particular condition (for example, to wait for an HTTP health check endpoint to send back a 200 response), you can implement custom wait logic in your fixtures.

Here's an example of a fixture called wait_for_api that waits for an HTTP service to come online before a test called test_read_and_write can run.

import pytest
import requests
from urllib.parse import urljoin
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter

# Invoking this fixture: 'function_scoped_container_getter' starts all services
@pytest.fixture(scope="function")
def wait_for_api(function_scoped_container_getter):
    """Wait for the api from my_api_service to become responsive"""
    request_session = requests.Session()
    retries = Retry(total=5,
                    backoff_factor=0.1,
                    status_forcelist=[500, 502, 503, 504])
    request_session.mount('http://', HTTPAdapter(max_retries=retries))

    service = function_scoped_container_getter.get("my_api_service").network_info[0]
    api_url = "http://%s:%s/" % (service.hostname, service.host_port)
    assert request_session.get(api_url)
    return request_session, api_url


def test_read_and_write(wait_for_api):
    """The Api is now verified good to go and tests can interact with it"""
    request_session, api_url = wait_for_api
    data_string = 'some_data'
    request_session.put('%sitems/2?data_string=%s' % (api_url, data_string))
    item = request_session.get(urljoin(api_url, 'items/2')).json()
    assert item['data'] == data_string
    request_session.delete(urljoin(api_url, 'items/2'))

Use wider scoped fixtures

The function_scoped_container_getter fixture uses "function" scope, meaning that all of the containers are torn down after each individual test.

This is done so that every test gets to run in a "clean" environment. However, this can potentially make a test suite take a very long time to complete.

There are two options to make containers persist beyond a single test. The best way is to use the fixtures that are explicitly scoped to different scopes. There are three additional fixtures for this purpose: class_scoped_container_getter, module_scoped_container_getter and session_scoped_container_getter. Notice that you need to be careful when using these! There are two main caveats to keep in mind:

  1. Manage your scope correctly, using 'module' scope and 'function' scope in one single file will throw an error! This is because the module scoped fixture will spin up the containers and then the function scoped fixture will try to spin up the containers again. Docker compose does not allow you to spin up containers twice.
  2. Clean up your environment after each test. Because the containers are not restarted their environments can carry the information from previous tests. Therefore you need to be very careful when designing your tests such that they leave the containers in the same state that it started in or you might run into difficult to understand behaviour.

A second method to make containers persist beyond a single test is to supply the --use-running-containers flag to pytest like so:

pytest --use-running-containers

With this flag, pytest-docker-compose checks that all containers are running during the project creation. If they are not running a warning is given and they are spun up anyways. They are then used for all the tests and NOT TORN DOWN afterwards.

This mode is best used in combination with the --docker-compose-no-build flag since the newly build containers won't be used anyways. like so:

pytest --docker-compose-no-build --use-running-containers

It is of course possible to add these options to pytest.ini or pyproject.toml.

Notice that for this mode the scoping of the fixtures becomes less important since the containers are fully persistent throughout all tests. I only recommend using this if your network takes excessively long to spin up/tear down. It should really be a last resort and you should probably look into speeding up your network instead of using this.

Running Integration Tests

Use pytest to run your tests as normal:

pytest

By default, this will look for a docker-compose.yml file in the current working directory. You can specify a different file via the --docker-compose option:

pytest --docker-compose=/path/to/docker-compose.yml

Docker compose allows for specifying multiple compose files as described in the docs. To specify more than one compose file, separate them with a ,:

pytest --docker-compose=/path/to/docker-compose.yml,/another/docker-compose.yml,/third/docker-compose.yml

Tip

Alternatively, you can specify this option in your pytest.ini file:

[pytest]
addopts = --docker-compose=/path/to/docker-compose.yml

The option will be ignored for tests that do not use this plugin.

See Configuration Options for more information on using configuration files to modify pytest behavior.

Remove volumes after tests

There is another configuration option that will delete the volumes of containers after running.

pytest --docker-compose-remove-volumes

This option will be ignored if the plugin is not used. Again, this option can also be added to the pytest.ini file.

Command Line Options Summary

Option Description
--docker-compose Path to docker-compose.yml file or directory containing one. Multiple files can be specified with commas.
--docker-compose-no-build Skip building Docker images before running tests.
--docker-compose-remove-volumes Remove container volumes after tests complete.
--use-running-containers Use already running containers instead of starting new ones.
--docker-compose-wait Wait for services to be healthy before running tests (requires healthcheck definitions).
--docker-compose-wait-timeout Timeout in seconds when waiting for services to be healthy.

For more examples on how to use this plugin look at the testing suite of this plugin itself! It will give you some examples for configuring pyproject.toml and how to use the different fixtures to run docker containers.

Releasing

Release commands are managed with just, which is installed by the development dependency group:

uv sync

Preview the release notes for a version without modifying the repository:

uv run just release-notes 0.3.0

Create and push a release from a clean, up-to-date main branch by choosing a semantic version increment:

uv run just release patch
uv run just release minor
uv run just release major

The command runs the tests, updates the package version, creates the version commit and tag, and pushes both. Once GitHub Actions has created the GitHub Release, its generated notes can be added with:

uv run just publish-release-notes 0.3.0

Release files for pytest-docker-compose-v2 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-docker-compose-v2 0.3.0
File Size Uploaded
pytest_docker_compose_v2-0.3.0.tar.gz 99.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-docker-compose-v2 0.3.0
File Interpreter ABI Platform
pytest_docker_compose_v2-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 113.2 kB

Release files / pytest_docker_compose_v2-0.3.0.tar.gz

Download URL pytest_docker_compose_v2-0.3.0.tar.gz
Size 99.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b2be22c9b119229965df17d240fe392182cc02d8e1c8595074b1beb8b5e190c5
BLAKE2b-256 checksum
How to use checksums
408b930c52a19373684a0d647939bed47fad2921ae1da77684683328cd10e0e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pytest_docker_compose_v2-0.3.0-py3-none-any.whl

Download URL pytest_docker_compose_v2-0.3.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7fc880137962c078d8d77bca76f333038d3bbd92382c28e11a7c9597ad94614f
BLAKE2b-256 checksum
How to use checksums
043ce76c9ed877aa1bdc292eb2ce39b66d1c8a8ebcbc0dfc1084883570a15b6a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page