Run container-structure-test from pytest!
Project description
pytest-container-structure-test
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_BINARYat 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:
$VARor${VAR}— expands from the environment; referencing an unset variable is a collection error.${VAR:-default}— usesdefaultwhenVARis 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/amd64replaces a locallinux/arm64image under the same tag. When testing multiple platforms, setpull = trueso 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-onlynever 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; setPYTEST_CONTAINER_STRUCTURE_TEST_BINARYto use a specificcontainer-structure-testbinary instead.
License
pytest-container-structure-test is distributed under the terms of the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_container_structure_test-0.0.2.tar.gz.
File metadata
- Download URL: pytest_container_structure_test-0.0.2.tar.gz
- Upload date:
- Size: 23.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86f7cf0a34e3e98b18270c7bbd288ce03ba991ba7b1d3c4ff3f09eae40f91104
|
|
| MD5 |
1e30e9e8679a63476ff6cb0294076f79
|
|
| BLAKE2b-256 |
ab44a90c7da1e6049b6f48db2abdbad9436ed1cf9c208cba1e7ef7b960cd423f
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_container_structure_test-0.0.2.tar.gz -
Subject digest:
86f7cf0a34e3e98b18270c7bbd288ce03ba991ba7b1d3c4ff3f09eae40f91104 - Sigstore transparency entry: 2047606812
- Sigstore integration time:
-
Permalink:
FlavioAmurrioCS/pytest-container-structure-test@5e23999fe80f971b351171bc4b1d36e7a60a1f2d -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/FlavioAmurrioCS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yaml@5e23999fe80f971b351171bc4b1d36e7a60a1f2d -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytest_container_structure_test-0.0.2-py3-none-any.whl.
File metadata
- Download URL: pytest_container_structure_test-0.0.2-py3-none-any.whl
- Upload date:
- Size: 13.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30575168547f0fa2014d71c3bf79cad2fa4565b03fc675472fb1a824a7607162
|
|
| MD5 |
6cd29bd878ae1915caef56e9c0bd07f8
|
|
| BLAKE2b-256 |
2fb035fc65989f1d5ff6b363df3aa4ec7b8ce8adb4fb509e65c97b7fab7b6539
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_container_structure_test-0.0.2-py3-none-any.whl -
Subject digest:
30575168547f0fa2014d71c3bf79cad2fa4565b03fc675472fb1a824a7607162 - Sigstore transparency entry: 2047607382
- Sigstore integration time:
-
Permalink:
FlavioAmurrioCS/pytest-container-structure-test@5e23999fe80f971b351171bc4b1d36e7a60a1f2d -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/FlavioAmurrioCS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yaml@5e23999fe80f971b351171bc4b1d36e7a60a1f2d -
Trigger Event:
push
-
Statement type: