Snapshot testing pytest plugin with minimal ceremony and flexible recorders.
Project description
pytest-ditto
Snapshot testing pytest plugin with minimal ceremony and flexible recorders.
Introduction
The pytest-ditto plugin is intended to be used for snapshot/regression testing. There are
two key components: the snapshot fixture and snapshot recorders.
The snapshot Fixture
In the following basic example, the function to test is fn, the test is using the
snapshot fixture and it is asserting that the result of calling fn with the
value of x does not change.
import ditto
def fn(x: int) -> int:
return x + 1 # original implementation
# return x + 2 # new implementation
def test_fn(snapshot) -> None:
x = 1
result = fn(x)
assert result == snapshot(result, key="fn")
The first time the test is run, the snapshot fixture takes the data passed to it and
persists it to a .ditto directory in the same location as the test module. Subsequent
test runs load the stored file and use that value for comparison.
By default, snapshot data is persisted using pickle; however, a range of recorders
can be selected per test using ditto marks.
@ditto Marks
If the default recorder (pickle) isn't appropriate, a different recorder can be
specified per test using ditto marks — customised pytest mark decorators.
The built-in recorders are:
| Mark | Recorder | File extension |
|---|---|---|
@ditto.pickle |
pickle | .pkl |
@ditto.yaml |
yaml | .yaml |
@ditto.json |
json | .json |
@ditto.record("name") |
any registered recorder | varies |
@ditto.pickle, @ditto.yaml, and @ditto.json are convenience shorthands for
@ditto.record("pickle"), @ditto.record("yaml"), and @ditto.record("json")
respectively.
Additional recorders can be installed via plugins:
| Recorders | Plugin | Marks |
|---|---|---|
pandas |
pytest-ditto-pandas |
|
pyarrow |
pytest-ditto-pyarrow |
|
Usage
pd.DataFrame
Install pytest-ditto[pandas] to make pandas recorders available.
import pandas as pd
import ditto
def awesome_fn_to_test(df: pd.DataFrame):
df.loc[:, "a"] *= 2
return df
@ditto.pandas.parquet
def test_fn_with_parquet_dataframe_snapshot(snapshot):
input_data = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 9]})
result = awesome_fn_to_test(input_data)
pd.testing.assert_frame_equal(result, snapshot(result, key="ab_dataframe"))
@ditto.pandas.json
def test_fn_with_json_dataframe_snapshot(snapshot):
input_data = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 9]})
result = awesome_fn_to_test(input_data)
pd.testing.assert_frame_equal(result, snapshot(result, key="ab_dataframe"))
For the above example the snapshot files would be found in the following locations:
.ditto/test_fn_with_parquet_dataframe_snapshot@ab_dataframe.pandas.parquet.ditto/test_fn_with_json_dataframe_snapshot@ab_dataframe.pandas.json
pyarrow.Table
Install pytest-ditto[pyarrow] to make pyarrow recorders available.
import pyarrow as pa
import pyarrow.compute as pc
import ditto
import pytest
@pytest.fixture
def table() -> pa.Table:
return pa.table(
[
[1, 2, 3, 4],
[4.5, 5.2, 6.8, 3.5],
[7, 8.5, None, None],
[True, False, True, True],
["a", "b", "c", "x"],
],
names=list("abcde"),
)
def fn(x: pa.Table):
even_filter = (pc.bit_wise_and(pc.field("a"), pc.scalar(1)) == pc.scalar(0))
return x.filter(even_filter)
@ditto.pyarrow.parquet
def test_fn_with_pyarrow_parquet_snapshot(snapshot, table):
result = fn(table)
assert result.equals(snapshot(result, key="filtered"))
For the above example the snapshot files would be found in the following location:
.ditto/test_fn_with_pyarrow_parquet_snapshot@filtered.pyarrow.parquet
unittest.TestCase
DittoTestCase provides the snapshot fixture as a cached_property for use with
unittest.TestCase:
import unittest
from ditto import DittoTestCase
def fn(x: int) -> int:
return x + 1
class TestFn(DittoTestCase):
def test_fn(self):
result = fn(1)
assert result == self.snapshot(result, key="fn")
Snapshot files are placed in a .ditto directory adjacent to the test file, using the
fully-qualified test method name as the group name.
Custom Recorders
A Recorder is a frozen dataclass pairing a file extension with save and load
functions. Plugin packages register Recorder instances via the ditto_recorders
entry point group.
from pathlib import Path
from ditto.recorders import Recorder
def _save(data: MyType, filepath: Path) -> None:
... # write data to filepath
def _load(filepath: Path) -> MyType:
... # read and return data from filepath
my_recorder: Recorder[MyType] = Recorder(
extension="myformat",
save=_save,
load=_load,
)
Register it in pyproject.toml:
[project.entry-points.ditto_recorders]
my_recorder = "my_package.recorders:my_recorder"
Once registered, the recorder is available by name via @ditto.record("my_recorder").
Plugin marks (e.g. @ditto.myplugin.myformat) can also be registered via the
ditto_marks entry point group.
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_ditto-1.0.1.tar.gz.
File metadata
- Download URL: pytest_ditto-1.0.1.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8bc153a7ae00a7321f00113b1c1cc0833cb18686bb1a96a26f4906119a94427a
|
|
| MD5 |
88e2a894c8b17451acf6bb84cbc82b6d
|
|
| BLAKE2b-256 |
69ba4696862e2b420588a52e81009941d083041fc85e36d806f71be2cb72abf2
|
Provenance
The following attestation bundles were made for pytest_ditto-1.0.1.tar.gz:
Publisher:
release.yml on owlowlyowl/pytest-ditto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_ditto-1.0.1.tar.gz -
Subject digest:
8bc153a7ae00a7321f00113b1c1cc0833cb18686bb1a96a26f4906119a94427a - Sigstore transparency entry: 1153142862
- Sigstore integration time:
-
Permalink:
owlowlyowl/pytest-ditto@bb850ed10f42fb40d68e1da7e155c5ce720c2a45 -
Branch / Tag:
refs/tags/1.0.1 - Owner: https://github.com/owlowlyowl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb850ed10f42fb40d68e1da7e155c5ce720c2a45 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytest_ditto-1.0.1-py3-none-any.whl.
File metadata
- Download URL: pytest_ditto-1.0.1-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
823fdb93c48bb70c4acee2747550090d869c7a233e16b06962bc3491788df044
|
|
| MD5 |
87946b9f24311ce9b8dd08caa7b01f4c
|
|
| BLAKE2b-256 |
23fc9734655dafa0e79e5ea99ee4cd85da466b996d4fd8d5c8e5e277d3f2ce77
|
Provenance
The following attestation bundles were made for pytest_ditto-1.0.1-py3-none-any.whl:
Publisher:
release.yml on owlowlyowl/pytest-ditto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_ditto-1.0.1-py3-none-any.whl -
Subject digest:
823fdb93c48bb70c4acee2747550090d869c7a233e16b06962bc3491788df044 - Sigstore transparency entry: 1153142875
- Sigstore integration time:
-
Permalink:
owlowlyowl/pytest-ditto@bb850ed10f42fb40d68e1da7e155c5ce720c2a45 -
Branch / Tag:
refs/tags/1.0.1 - Owner: https://github.com/owlowlyowl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb850ed10f42fb40d68e1da7e155c5ce720c2a45 -
Trigger Event:
release
-
Statement type: