Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Icons:
- 💥 Breaking change
- 🔄 Changed

## [0.9.0] - 2025-10-23
- 🆕 Added class method for handling objects in LoadSnapshot fixture
- 🐞 Fix snappylapy logo not showing correctly in documentation

## [0.8.0] - 2025-10-18
- 🆕 Used integration with toolit to make snappylapy commands available for AI coding assistants
- 🆕 Add a `@configure_snappylapy()` decorator as an alternative for using @pytest.mark.snappylapy for improved documentation support, type checking and IDE integration.
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div align="center">
<img src="snappylapy-logo.svg" alt="Snappylapy Logo" style="width:200px;"/>
<img src="https://raw.githubusercontent.com/martinmoldrup/snappylapy/refs/heads/main/docs/snappylapy-logo.svg" alt="Snappylapy Logo" style="width:200px;"/>

<h1> Snappylapy</h1>
<h3>Pytest Plugin for Snapshot Testing</h3>
Expand All @@ -26,7 +26,7 @@ Effortlessly capture, diff, and reuse complex, non-deterministic, and AI-generat

---

Welcome to **Snappylapy**, a powerful and intuitive snapshot testing plugin for Python's pytest framework. Snappylapy simplifies the process of capturing and verifying snapshots of your data, ensuring your code behaves as expected across different runs. With Snappylapy, you can save snapshots in a human-readable format and deserialize them for robust integration testing, providing a clear separation layer to help isolate errors and maintain code integrity.
Welcome to **Snappylapy**, a powerful and intuitive snapshot testing plugin for Python's pytest framework. Snappylapy simplifies the process of capturing and verifying snapshots of your data, ensuring your code behaves as expected across different test case runs. With Snappylapy, you can save snapshots in a human-readable format and deserialize them for robust integration testing, providing a clear separation layer to help isolate errors and maintain code integrity.

Snappylapy is following the api-style of the very popular Jest testing framework, making it familiar and easy to use for JavaScript developers.

Expand Down Expand Up @@ -118,6 +118,9 @@ In snappylapy, you can customize the output directory for your snapshots. This i

`test_expect_snapshot_custom_dir.py`
```python
import pytest
from snappylapy import Expect

@pytest.mark.snappylapy(output_dir="custom_dir")
def test_snapshot_with_custom_directories(expect: Expect):
"""Test snapshot with custom directories."""
Expand Down Expand Up @@ -152,15 +155,20 @@ When we add the `foreach_folder_in` parameter to the `snappylapy` marker, it wil
`test_expect_snapshot_multiple_folders.py`
```python
import json
import pytest
import pathlib
from snappylapy import Expect


def transform_data(data: dict) -> dict:
"""A sample transformation function."""
# Example transformation: add a new key-value pair
data["transformed"] = True
return data


@pytest.mark.snappylapy(foreach_folder_in="test_data")
def test_snapshot_parametrized_multiple_test_case_folders(test_directory: pathlib.Path, expect: Expect):
def test_snapshot_parametrized_multiple_test_case_folders(test_directory: pathlib.Path, expect: Expect) -> None:
"""Test snapshot with multiple folders."""
data = json.loads((test_directory / "input.json").read_text())
transformed_data = transform_data(data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"py/object": "test_load_snapshot_from_file_object.Custom",
"value": 42
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World
File renamed without changes
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ authors = [
{name = "Martin Møldrup"},
]
requires-python = "<4.0,>=3.9"
version = "0.8.0"
version = "0.9.0"
description = "A snapshot library for python optimized for easy of use, human readable snapshots and enabling decoupling of chained integration tests."
readme = "README.md"
classifiers = [
Expand Down
33 changes: 32 additions & 1 deletion snappylapy/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ class Expect:
---------------
`test_expect_direct_call.py`
```python
import pytest
from snappylapy.fixtures import Expect

def test_expect_direct_call(expect: Expect) -> None:
Expand Down Expand Up @@ -536,3 +535,35 @@ def test_load_snapshot_dataframe(load_snapshot: LoadSnapshot) -> None:
"""
self.settings.depending_filename_extension = "dataframe.csv"
return PandasCsvSerializer().deserialize(self._read_snapshot())

def object(self) -> object:
"""
Load object snapshot.

Use this method to load a generic object snapshot that was created in a previous test.
This is useful for reusing test data, isolating dependencies, and verifying integration between components.

Example usage:
--------------
`test_load_snapshot_from_file_object.py`
```python
import pytest
from snappylapy import Expect, LoadSnapshot

class Custom:
def __init__(self) -> None:
self.value = 42

def test_save_object_snapshot(expect: Expect) -> None:
obj = Custom()
expect(obj).to_match_snapshot()

@pytest.mark.snappylapy(depends=[test_save_object_snapshot])
def test_load_snapshot_object(load_snapshot: LoadSnapshot) -> None:
obj = load_snapshot.object()
assert hasattr(obj, "value")
assert obj.value == 42
```
"""
self.settings.depending_filename_extension = "object.json"
return JsonPickleSerializer[object]().deserialize(self._read_snapshot())
7 changes: 7 additions & 0 deletions tests/doc_examples/README/test_expect_snapshot_custom_dir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import pytest
from snappylapy import Expect

@pytest.mark.snappylapy(output_dir="custom_dir")
def test_snapshot_with_custom_directories(expect: Expect):
"""Test snapshot with custom directories."""
expect.string("Hello World").to_match_snapshot()
19 changes: 19 additions & 0 deletions tests/doc_examples/README/test_expect_snapshot_multiple_folders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import json
import pytest
import pathlib
from typing import Any


def transform_data(data: dict) -> dict:
"""A sample transformation function."""
# Example transformation: add a new key-value pair
data["transformed"] = True
return data

@pytest.mark.skip
@pytest.mark.snappylapy(foreach_folder_in="test_data")
def test_snapshot_parametrized_multiple_test_case_folders(test_directory: pathlib.Path, expect: Any) -> None:
"""Test snapshot with multiple folders."""
data = json.loads((test_directory / "input.json").read_text())
transformed_data = transform_data(data)
expect.dict(transformed_data).to_match_snapshot()
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from snappylapy.fixtures import Expect


def test_expect_direct_call(expect: Expect) -> None:
# Dict input
data_dict: dict[str, int] = {"a": 1, "b": 2}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import pytest
from snappylapy.fixtures import Expect

try:
import pandas as pd # noqa: F401
pandas_installed = True
import pandas as pd

HAS_PANDAS = True
except ImportError:
pandas_installed = False
from snappylapy.fixtures import Expect
HAS_PANDAS = False


@pytest.mark.skipif(not pandas_installed, reason="pandas is not installed")
@pytest.mark.skipif(not HAS_PANDAS, reason="pandas is not installed")
def test_expect_dataframe(expect: Expect) -> None:
"""Test that dataframe matches the snapshot."""
df: pd.DataFrame = pd.DataFrame({"key": ["value1", "value2"]})
expect.dataframe(df).to_match_snapshot()
expect.dataframe(df).to_match_snapshot()
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import pytest
from snappylapy.fixtures import LoadSnapshot, Expect
from snappylapy.fixtures import Expect, LoadSnapshot

try:
import pandas as pd # noqa: F401
pandas_installed = True
import pandas as pd
HAS_PANDAS = True
except ImportError:
pandas_installed = False
HAS_PANDAS = False

@pytest.mark.skipif(not pandas_installed, reason="pandas is not installed")
@pytest.mark.skipif(not HAS_PANDAS, reason="pandas is not installed")
def test_save_dataframe_snapshot(expect: Expect) -> None:
"""Test saving a dataframe snapshot."""
df: pd.DataFrame = pd.DataFrame({"numbers": [1, 2, 3]})
expect(df).to_match_snapshot()

@pytest.mark.skipif(not pandas_installed, reason="pandas is not installed")
@pytest.mark.skipif(not HAS_PANDAS, reason="pandas is not installed")
@pytest.mark.snappylapy(depends=[test_save_dataframe_snapshot])
def test_load_snapshot_dataframe(load_snapshot: LoadSnapshot) -> None:
"""Test loading a dataframe snapshot."""
df: pd.DataFrame = load_snapshot.dataframe()
assert df["numbers"].sum() == 6
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
from snappylapy import Expect, LoadSnapshot


class Custom:
def __init__(self) -> None:
self.value = 42


def test_save_object_snapshot(expect: Expect) -> None:
obj = Custom()
expect(obj).to_match_snapshot()


@pytest.mark.snappylapy(depends=[test_save_object_snapshot])
def test_load_snapshot_object(load_snapshot: LoadSnapshot) -> None:
obj = load_snapshot.object()
assert hasattr(obj, "value")
assert obj.value == 42