Storage Backends¶
By default, snapshots are stored in a .ditto/ directory next to each test
file. The storage target can be overridden at several levels.
Priority Order¶
From highest to lowest priority:
Per-test: target=¶
Specify a URI directly in the mark:
import ditto
# Local path relative to this test file
@ditto.record("json", target="file://snapshots/group_a")
def test_foo(snapshot): ...
# S3 bucket
@ditto.record("yaml", target="s3://my-bucket/ci-snapshots/")
def test_bar(snapshot): ...
# In-memory (ephemeral, no disk I/O)
@ditto.record("json", target="memory://")
def test_baz(snapshot): ...
# Registered non-fsspec backend
@ditto.record("json", target="postgresql://db-host/mydb")
def test_qux(snapshot): ...
target= accepts any URI whose scheme is:
- A supported fsspec protocol (file, s3, gcs, memory, etc.)
- A scheme registered via the
ditto_backendsentry-point group
Relative file:// paths resolve relative to the test file's directory.
Authentication: ditto_storage_options¶
Credentials for remote backends belong in conftest.py, not in marks:
# conftest.py
import os
import pytest
@pytest.fixture(scope="session")
def ditto_storage_options():
return {
"s3": {"key": os.environ["AWS_KEY"], "secret": os.environ["AWS_SECRET"]},
"postgresql": {"password": os.environ["PGPASSWORD"]},
"redis": {"password": os.environ["REDIS_PASSWORD"]},
}
Values are passed as kwargs to fsspec.core.url_to_fs for fsspec schemes,
or to the registered backend factory for custom schemes.
Project-wide: ditto_target ini¶
Set a default target for all tests in pyproject.toml:
Individual target= marks take precedence over this setting.
Named Profiles: target_profile=¶
Profiles are reusable, named targets. Useful when:
- A suite routes to a small set of stable backends
- Two targets share a scheme but need different credentials
Defining Profiles¶
In a fixture (dynamic, with secrets):
# conftest.py
import os
import pytest
@pytest.fixture(scope="session")
def ditto_target_profiles():
return {
"golden": "s3://my-bucket/golden/",
"s3_east": {
"uri": "s3://east-bucket/golden/",
"storage_options": {
"key": os.environ["AWS_KEY"],
"secret": os.environ["AWS_SECRET"],
},
},
}
In pyproject.toml (static):
[tool.pytest-ditto.target_profiles]
golden = "s3://my-bucket/golden/"
[tool.pytest-ditto.target_profiles.s3_east]
uri = "s3://east-bucket/golden/"
storage_options = { key = "...", secret = "..." }
Static profiles are only read from pyproject.toml
The [tool.pytest-ditto.target_profiles] table is read from the
pyproject.toml in pytest's rootdir,
and nowhere else:
pytest.ini,tox.iniandsetup.cfgcannot hold the table.- If pytest is configured by one of those files, a
pyproject.tomlnext to it (in the rootdir) is still read. - A
pyproject.tomlin any other directory, such as a subdirectory of the rootdir, is ignored.
Projects without a pyproject.toml in the rootdir can define the same
profiles in the ditto_target_profiles fixture instead. Selecting a
default profile with the ditto_target_profile ini option works from any
pytest configuration file.
Using Profiles¶
Per-test:
Project-wide:
Profile Rules¶
- A profile value is either a URI string or a mapping with
uriand optionalstorage_options - Profiles do not read
ditto_storage_options— they are self-contained target=andtarget_profile=are mutually exclusive on a markditto_targetandditto_target_profileare mutually exclusive in ini- A name defined in both fixture and
pyproject.tomlraises an error - Static profiles are read only from the rootdir's
pyproject.toml(see the note above)