Pytest snapshot testing utility that enables developers to write tests asserting immutability of computed results.
npx @tessl/cli install tessl/pypi-syrupy@4.9.00
# Syrupy
1
2
A zero-dependency pytest snapshot testing library that enables developers to write tests asserting immutability of computed results. Syrupy provides an extensible and idiomatic approach to snapshot testing with the syntax `assert x == snapshot`, offering comprehensive filtering, matching, and custom serialization capabilities.
3
4
## Package Information
5
6
- **Package Name**: syrupy
7
- **Language**: Python
8
- **Installation**: `pip install syrupy`
9
- **Requirements**: Python >=3.8.1, pytest >=7.0.0,<9.0.0
10
11
## Core Imports
12
13
```python
14
import syrupy
15
```
16
17
Standard usage in tests:
18
19
```python
20
def test_example(snapshot):
21
assert result == snapshot
22
```
23
24
Extension imports:
25
26
```python
27
from syrupy.extensions.amber import AmberSnapshotExtension
28
from syrupy.extensions.json import JSONSnapshotExtension
29
from syrupy.extensions.image import PNGImageSnapshotExtension, SVGImageSnapshotExtension
30
from syrupy.extensions.single_file import SingleFileSnapshotExtension, SingleFileAmberSnapshotExtension, WriteMode
31
```
32
33
Matcher and filter imports:
34
35
```python
36
from syrupy.matchers import path_type, path_value, compose_matchers
37
from syrupy.filters import props, paths, paths_include
38
```
39
40
Exception imports:
41
42
```python
43
from syrupy.exceptions import SnapshotDoesNotExist, FailedToLoadModuleMember, TaintedSnapshotError
44
from syrupy.matchers import PathTypeError, StrictPathTypeError
45
```
46
47
## Basic Usage
48
49
```python
50
def test_basic_snapshot(snapshot):
51
"""Basic snapshot testing - creates .ambr file in __snapshots__ directory"""
52
actual = {"name": "Alice", "age": 30, "scores": [85, 92, 78]}
53
assert actual == snapshot
54
55
def test_with_index(snapshot):
56
"""Multiple snapshots in one test"""
57
assert "first result" == snapshot
58
assert "second result" == snapshot
59
60
def test_custom_extension(snapshot):
61
"""Using JSON extension for dictionary data"""
62
from syrupy.extensions.json import JSONSnapshotExtension
63
64
data = {"users": [{"id": 1, "name": "Alice"}]}
65
assert data == snapshot.use_extension(JSONSnapshotExtension)
66
67
def test_with_matchers(snapshot):
68
"""Using matchers to handle dynamic data"""
69
from syrupy.matchers import path_type
70
71
result = {
72
"timestamp": "2023-12-01T10:30:00Z", # Dynamic value
73
"user_id": 12345, # Dynamic value
74
"message": "Hello world" # Static value
75
}
76
77
# Replace dynamic values with placeholders
78
assert result == snapshot(matcher=path_type({
79
"timestamp": (str, "<timestamp>"),
80
"user_id": (int, "<user_id>")
81
}))
82
83
def test_with_filters(snapshot):
84
"""Using filters to include/exclude properties"""
85
from syrupy.filters import props
86
87
data = {
88
"public_field": "visible",
89
"private_field": "hidden",
90
"internal_field": "secret"
91
}
92
93
# Only include public fields
94
assert data == snapshot(include=props("public_field"))
95
```
96
97
## Architecture
98
99
Syrupy uses an extensible architecture built around several key components:
100
101
- **SnapshotAssertion**: Main assertion class providing fluent interface for configuration
102
- **Extensions**: Pluggable serialization and storage backends (Amber, JSON, PNG, SVG, etc.)
103
- **Matchers**: Value transformation system for handling dynamic data
104
- **Filters**: Property inclusion/exclusion system for controlling serialization scope
105
- **Session Management**: pytest integration handling snapshot lifecycle and reporting
106
107
The pytest plugin automatically registers hooks and provides the `snapshot` fixture, making snapshot testing seamlessly integrate with existing test suites.
108
109
## Capabilities
110
111
### Core Snapshot Assertions
112
113
Primary snapshot assertion functionality with fluent interface for configuration, including basic assertions, indexed snapshots, and method chaining for custom behavior.
114
115
```python { .api }
116
class SnapshotAssertion:
117
def __call__(self, *, matcher=None, include=None, exclude=None, extension_class=None): ...
118
def __eq__(self, other): ...
119
def use_extension(self, extension_class): ...
120
def with_defaults(self, **kwargs): ...
121
```
122
123
[Core Assertions](./core-assertions.md)
124
125
### Extensions System
126
127
Pluggable serialization and storage system supporting multiple output formats including Amber (default multi-snapshot), JSON, PNG, SVG, and raw file formats with extensible base classes.
128
129
```python { .api }
130
class AbstractSyrupyExtension: ...
131
class AmberSnapshotExtension(AbstractSyrupyExtension): ...
132
class JSONSnapshotExtension(AbstractSyrupyExtension): ...
133
class PNGImageSnapshotExtension(AbstractSyrupyExtension): ...
134
class SVGImageSnapshotExtension(AbstractSyrupyExtension): ...
135
```
136
137
[Extensions](./extensions.md)
138
139
### Matchers
140
141
Value transformation system for handling dynamic data in snapshots, providing path-based matching with type replacement and regex-based value substitution.
142
143
```python { .api }
144
def path_type(mapping: Dict[str, Tuple[type, Any]], *, strict: bool = True): ...
145
def path_value(mapping: Dict[str, Any]): ...
146
def compose_matchers(*matchers): ...
147
```
148
149
[Matchers](./matchers.md)
150
151
### Filters
152
153
Property inclusion and exclusion system for controlling serialization scope, supporting path-based and property-based filtering with nested path handling.
154
155
```python { .api }
156
def props(*included: str): ...
157
def paths(*included: str): ...
158
def paths_include(*nested_paths: str): ...
159
```
160
161
[Filters](./filters.md)
162
163
### CLI Integration
164
165
Command-line options and pytest integration providing snapshot update modes, reporting configuration, and extension selection.
166
167
```python { .api }
168
def pytest_addoption(parser): ...
169
@pytest.fixture
170
def snapshot(request): ...
171
```
172
173
[CLI Integration](./cli-integration.md)
174
175
## Types
176
177
```python { .api }
178
from typing import Union, Any, Callable, Tuple, Hashable, Type, List, Optional
179
import re
180
181
# Core types
182
SnapshotIndex = Union[int, str]
183
SerializableData = Any
184
SerializedData = Union[str, bytes]
185
186
# Property system types
187
PropertyName = Hashable
188
PropertyValueType = Type[SerializableData]
189
PropertyPathEntry = Tuple[PropertyName, PropertyValueType]
190
PropertyPath = Tuple[PropertyPathEntry, ...]
191
PropertyMatcher = Callable[[SerializableData, PropertyPath], Optional[SerializableData]]
192
PropertyFilter = Callable[[PropertyName, PropertyPath], bool]
193
194
# Matcher helper types
195
try:
196
MatchResult = Optional[re.Match[str]]
197
except TypeError:
198
MatchResult = Optional[re.Match]
199
Replacer = Callable[[SerializableData, MatchResult], SerializableData]
200
201
# Assertion result
202
@dataclass
203
class AssertionResult:
204
snapshot_location: str
205
snapshot_name: str
206
asserted_data: Optional[SerializedData]
207
recalled_data: Optional[SerializedData]
208
created: bool
209
updated: bool
210
success: bool
211
exception: Optional[Exception]
212
test_location: "PyTestLocation"
213
214
@property
215
def final_data(self) -> Optional[SerializedData]: ...
216
217
# Diff mode
218
class DiffMode(Enum):
219
DETAILED = "detailed"
220
DISABLED = "disabled"
221
```
222
223
## Exceptions
224
225
```python { .api }
226
class SnapshotDoesNotExist(Exception):
227
"""Raised when a snapshot file or entry doesn't exist"""
228
229
class FailedToLoadModuleMember(Exception):
230
"""Raised when unable to load a module member"""
231
232
class TaintedSnapshotError(Exception):
233
"""Raised when snapshot is corrupted and needs regeneration"""
234
235
class PathTypeError(TypeError):
236
"""Raised when path type matching encounters an error"""
237
238
class StrictPathTypeError(PathTypeError):
239
"""Raised when strict path type matching fails due to type mismatch"""
240
```