Skip to main content

Seamlessly run pytest tests inside docker containers

Project description

pytest-in-docker

pytest-in-docker

Teleport your pytest tests into Docker containers.

PyPI CI Python License Discord


Install

pip install pytest-in-docker

Or with uv:

uv add pytest-in-docker

Quick Start

Decorate any test function. It runs inside a Docker container:

from pytest_in_docker import in_container
import platform

@in_container("python:alpine")
def test_runs_on_alpine():
    info = platform.freedesktop_os_release()  # platform is available in the container.
    assert info["ID"] == "alpine"

Then run pytest as usual:

pytest

The function is serialized with cloudpickle, sent to a fresh python:alpine container, and the result is reported back to your terminal.

Usage

The marker API integrates with all standard pytest features — fixtures, parametrize, and reporting work as expected.

Build from a Dockerfile

Point to a directory containing a Dockerfile and provide a tag. The image is built before the test runs:

import subprocess

@pytest.mark.in_container(path="./docker", tag="my-test-image:latest")
def test_custom_image():
    result = subprocess.run(["cat", "/etc/os-release"], capture_output=True, text=True)
    assert "alpine" in result.stdout.lower()

This works with the marker too:

@pytest.mark.in_container(path="./docker", tag="my-test-image:latest")
def test_custom_image_with_marker():
    ...

Test Across Multiple Images

Combine @pytest.mark.parametrize with the marker to run the same test across different containers. Use image as the parameter name — the plugin picks it up automatically:

import pytest
import platform

@pytest.mark.parametrize(
    ("image", "expected_id"),
    [
        ("python:alpine", "alpine"),
        ("python:slim", "debian"),
    ],
)
@pytest.mark.in_container()
def test_across_distros(image: str, expected_id: str):
    info = platform.freedesktop_os_release()
    assert info["ID"].lower() == expected_id

When @pytest.mark.in_container() is called with no arguments, it reads the image parameter from @pytest.mark.parametrize. This lets you build a compatibility matrix with zero boilerplate.

Custom Container Factory

When you need to customise the container beyond what the other modes offer — environment variables, volumes, extra ports — pass a factory:

from contextlib import contextmanager
import os
from typing import Iterator

from testcontainers.core.container import DockerContainer

from pytest_in_docker import in_container


@contextmanager
def my_container(port: int) -> Iterator[DockerContainer]:
    with (
        DockerContainer("python:alpine")
        .with_command("sleep infinity")
        .with_exposed_ports(port)
        .with_env("APP_ENV", "test") as container
    ):
        container.start()
        yield container


@pytest.mark.in_container(factory=my_container)
def test_env_is_set():
    assert os.environ["APP_ENV"] == "test"

A factory is a callable that accepts a port: int argument and returns a context manager yielding an already-started DockerContainer. The framework passes the communication port automatically — the factory just needs to expose it and run sleep infinity.

Timeouts

Tests running inside containers default to a 30-second timeout. If pytest-timeout is installed, its timeout ini setting and @pytest.mark.timeout marker are respected automatically:

import pytest

@pytest.mark.timeout(60)
@pytest.mark.in_container("python:alpine")
def test_slow_operation():
    ...

How It Works

When a decorated test runs:

Host (pytest)                         Docker Container
─────────────                         ────────────────
1. Spin up container           ──────>  python:alpine starts
2. Install deps                ──────>  pip install cloudpickle rpyc pytest
3. Start RPyC server           ──────>  listening on port 51337
4. Serialize test (cloudpickle)
5. Send bytes over RPyC        ──────>  deserialize + execute
         <────── result (pass/fail/exception) ──────
6. Container stops

How serialization works: cloudpickle serializes your test function — including closures, lambdas, and locally-defined helpers — into bytes on the host. Those bytes are sent to the container over RPyC, deserialized with the standard pickle module, and executed natively.

This means:

  • Your test code runs natively inside the container — not through docker exec or shell commands
  • Full Python semantics: imports, exceptions, and return values all work naturally
  • Closures and lambdas serialize correctly — use helper functions, captured variables, and dynamic code freely
  • pytest assertion introspection still works on the host side for reporting

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_in_docker-0.2.1.tar.gz (3.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_in_docker-0.2.1-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

Details for the file pytest_in_docker-0.2.1.tar.gz.

File metadata

  • Download URL: pytest_in_docker-0.2.1.tar.gz
  • Upload date:
  • Size: 3.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pytest_in_docker-0.2.1.tar.gz
Algorithm Hash digest
SHA256 5f25ceb12eb98a495c1f9f4764a73dd7e86b1213e6f1324f8f9c222ae34a00b5
MD5 c55f994a8c69de9612fe0a5a0e4baeec
BLAKE2b-256 f6cedfb5a8cb7dfb317b5cf27bc03c91b9f5b9d0537da893369a78dc8219d0ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_in_docker-0.2.1.tar.gz:

Publisher: publish.yml on mesa-dot-dev/pytest-in-docker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_in_docker-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_in_docker-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a903c3d903985fb6d446eb184a58e6fea725d49f916c18ce2762375b037c85e1
MD5 f076f01c3caa80827c5154630b9b4217
BLAKE2b-256 2f14180cf34e1ccb81aef0e683b1d2db3c1e292cf886eb56316b229c6f7e6f3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_in_docker-0.2.1-py3-none-any.whl:

Publisher: publish.yml on mesa-dot-dev/pytest-in-docker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page