Skip to main content

Run container-structure-test from pytest!

Project description

pytest-container-structure-test

PyPI - Version PyPI - Python Version pre-commit.ci status


Run container-structure-test configs from pytest, with every test in your YAML config reported as an individual pytest test — right alongside your regular Python tests.

$ pytest -v
structure.yaml::command:os-release PASSED                    [ 25%]
structure.yaml::command:gunicorn-installed PASSED            [ 50%]
structure.yaml::file-existence:app-dir PASSED                [ 75%]
test_app.py::test_healthcheck PASSED                         [100%]

Table of Contents

Installation

pip install pytest-container-structure-test

The plugin invokes the container-structure-test binary from your PATH. Any install route works:

  • upstream releases / brew (e.g. brew install container-structure-test)
  • pip install container-structure-test — the PyPI wheel that ships the binary as a console script in your environment
  • or point PYTEST_CONTAINER_STRUCTURE_TEST_BINARY at a specific binary

A running Docker daemon is required to execute the tests (not to collect them).

Usage

Declare your config files and the image each one targets in one place, using the container_structure_tests ini option (in pyproject.toml, pytest.ini, tox.ini, or setup.cfg). Each entry has the form <path/to/config.yaml>=<image>:

# pyproject.toml
[tool.pytest.ini_options]
container_structure_tests = [
  "tests/structure/web.yaml=myorg/web:${WEB_VERSION:-latest}",
  "tests/structure/db.yaml=${DB_IMAGE}",
  "tests/structure/cli.yaml=myorg/cli:latest",
]

Paths are relative to the pytest rootdir and must live under a directory pytest collects (typically tests/).

The image value supports environment-variable expansion, so the image name or tag can come from CI:

  • $VAR or ${VAR} — expands from the environment; referencing an unset variable is a collection error.
  • ${VAR:-default} — uses default when VAR is unset or empty, so runs work locally without exports.

Write your config files exactly as container-structure-test expects — nothing custom:

# tests/structure/web.yaml
schemaVersion: 2.0.0
commandTests:
  - name: gunicorn-installed
    command: gunicorn
    args: ["--version"]
fileExistenceTests:
  - name: app-dir
    path: /app
    shouldExist: true
metadataTest:
  exposedPorts: ["8000"]

Then just run pytest. Every entry in commandTests, fileExistenceTests, fileContentTests, and licenseTests — plus the metadataTest block — becomes its own pytest test with its own pass/fail, and failures include the errors, stdout, and stderr reported by the tool:

$ pytest -v tests/
tests/structure/web.yaml::command:gunicorn-installed PASSED
tests/structure/web.yaml::file-existence:app-dir FAILED
tests/structure/web.yaml::metadata PASSED
tests/test_app.py::test_healthcheck PASSED

=================================== FAILURES ===================================
______________________ structure/web.yaml::file-existence:app-dir _____________
File Existence Test: app-dir: FAIL
error: Expected file /app to exist but it does not

Test matrix: multiple images, configs, and platforms

When one image per config isn't enough — you want the same config against several images or architectures, or you need other container-structure-test test flags — declare suites in a plugin-owned table in pyproject.toml:

[[tool.pytest-container-structure-test.suites]]
configs   = ["tests/structure/web.yaml"]
image     = "myorg/web:${WEB_VERSION:-latest}"
platforms = ["linux/amd64", "linux/arm64"]
pull      = true

[[tool.pytest-container-structure-test.suites]]
configs    = ["tests/structure/base.yaml", "tests/structure/db.yaml"]
images     = ["${DB_IMAGE}", "myorg/db:edge"]
driver     = "docker"
extra_args = ["--save"]

Each suite expands to the cross product configs × images × platforms, and every combination is one container-structure-test invocation. When a config file runs in more than one combination, the differing dimensions show up as a suffix on each test's node ID:

tests/structure/web.yaml::command:gunicorn-installed[linux/amd64] PASSED
tests/structure/web.yaml::command:gunicorn-installed[linux/arm64] PASSED
tests/structure/db.yaml::command:psql-installed[myorg/db:edge] FAILED

Fields per suite:

Field Maps to Notes
config / configs --config one required; paths relative to rootdir
image / images --image one required; env-var expansion applies
platform / platforms --platform optional; omitted → host default
pull --pull boolean
driver --driver e.g. docker, tar, host
metadata --metadata path relative to rootdir; env-var expansion applies
extra_args passed verbatim any other flag, e.g. ["--save", "--runtime", "runsc"]

Unknown keys are rejected with a clear error (typo protection). extra_args may not include the flags the plugin itself manages (--config, --image, --platform, --output, --test-report, --no-color, --quiet) — overriding those would break result mapping.

[!NOTE] With the classic Docker image store, a tag holds one platform at a time — pulling linux/amd64 replaces a local linux/arm64 image under the same tag. When testing multiple platforms, set pull = true so each run fetches its own variant, and enable Docker's containerd image store if you want multi-platform tags cached side by side.

The simple container_structure_tests ini option keeps working and can be combined with suites; each of its entries is just a suite of one config, one image, and default flags.

How it works

  • Collection only parses the YAML — pytest --collect-only never touches Docker.
  • At run time, the binary is invoked once per config × image × platform combination and each collected test looks up its own result from that run's JSON report, so N tests in one config cost one image run per combination.
  • If the binary itself fails (Docker daemon down, image missing), every test in that config fails with the captured stderr.
  • The binary is resolved from PATH; set PYTEST_CONTAINER_STRUCTURE_TEST_BINARY to use a specific container-structure-test binary instead.

License

pytest-container-structure-test is distributed under the terms of the MIT license.

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_container_structure_test-0.0.2.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

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

File details

Details for the file pytest_container_structure_test-0.0.2.tar.gz.

File metadata

File hashes

Hashes for pytest_container_structure_test-0.0.2.tar.gz
Algorithm Hash digest
SHA256 86f7cf0a34e3e98b18270c7bbd288ce03ba991ba7b1d3c4ff3f09eae40f91104
MD5 1e30e9e8679a63476ff6cb0294076f79
BLAKE2b-256 ab44a90c7da1e6049b6f48db2abdbad9436ed1cf9c208cba1e7ef7b960cd423f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_container_structure_test-0.0.2.tar.gz:

Publisher: main.yaml on FlavioAmurrioCS/pytest-container-structure-test

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_container_structure_test-0.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_container_structure_test-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 30575168547f0fa2014d71c3bf79cad2fa4565b03fc675472fb1a824a7607162
MD5 6cd29bd878ae1915caef56e9c0bd07f8
BLAKE2b-256 2fb035fc65989f1d5ff6b363df3aa4ec7b8ce8adb4fb509e65c97b7fab7b6539

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_container_structure_test-0.0.2-py3-none-any.whl:

Publisher: main.yaml on FlavioAmurrioCS/pytest-container-structure-test

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