Pytest snapshot testing utility that enables developers to write tests asserting immutability of computed results.
Overall
score
80%
Property inclusion and exclusion system for controlling serialization scope in snapshots. Filters allow you to include or exclude specific properties from snapshot serialization, useful for ignoring dynamic fields, private data, or focusing on specific aspects of complex objects.
Filter properties by their names, supporting both inclusion and exclusion patterns.
def props(*prop_names: str) -> PropertyFilter:
"""
Create filter that includes only specified property names.
Parameters:
- *included: Property names to include in serialization
Returns:
PropertyFilter: Function that returns True for included properties
"""Usage examples:
def test_include_specific_props(snapshot):
from syrupy.filters import props
user_data = {
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"password_hash": "secret_hash_value",
"created_at": "2023-12-01T10:00:00Z",
"last_login": "2023-12-01T15:30:00Z",
"internal_id": "internal_abc123"
}
# Only include public fields in snapshot
assert user_data == snapshot(include=props("id", "name", "email"))
def test_exclude_sensitive_props(snapshot):
from syrupy.filters import props
config = {
"app_name": "MyApp",
"version": "1.0.0",
"database_url": "postgresql://localhost/mydb",
"secret_key": "super_secret_key_123",
"api_token": "token_abc123def456",
"debug": True
}
# Exclude sensitive configuration
assert config == snapshot(exclude=props("secret_key", "api_token", "database_url"))
def test_nested_object_filtering(snapshot):
from syrupy.filters import props
response = {
"user": {
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"private_notes": "Internal notes",
"password_hash": "secret"
},
"session": {
"id": "sess_123",
"token": "secret_token",
"expires": "2023-12-01T16:00:00Z"
}
}
# Include only public user fields
# Note: This filters at each level independently
assert response == snapshot(include=props("user", "id", "name", "email", "session", "expires"))Filter properties using full path strings delimited with dots, allowing precise control over nested structures.
def paths(*included: str) -> PropertyFilter:
"""
Create filter using full path strings for precise property targeting.
Parameters:
- *included: Dot-delimited path strings to include
Returns:
PropertyFilter: Function that returns True for included paths
"""Usage examples:
def test_precise_path_filtering(snapshot):
from syrupy.filters import paths
complex_data = {
"user": {
"profile": {
"name": "Alice",
"email": "alice@example.com",
"private_info": "secret"
},
"settings": {
"theme": "dark",
"notifications": True,
"private_key": "secret_key"
}
},
"metadata": {
"version": "1.0",
"debug_info": "internal_data"
}
}
# Include only specific nested paths
assert complex_data == snapshot(include=paths(
"user.profile.name",
"user.profile.email",
"user.settings.theme",
"metadata.version"
))
def test_exclude_specific_paths(snapshot):
from syrupy.filters import paths
api_response = {
"data": {
"users": [
{"id": 1, "name": "Alice", "internal_id": "int_123"},
{"id": 2, "name": "Bob", "internal_id": "int_456"}
]
},
"meta": {
"total_count": 2,
"request_id": "req_789",
"debug_trace": "trace_data"
}
}
# Exclude internal/debug paths
assert api_response == snapshot(exclude=paths(
"data.users.*.internal_id", # Exclude internal_id from all users
"meta.request_id",
"meta.debug_trace"
))Advanced filtering that automatically includes parent paths when targeting nested properties.
def paths_include(*path_parts: Union[Tuple[str, ...], List[str]]) -> PropertyFilter:
"""
Create include filter with automatic parent path inclusion.
When including a nested path, automatically includes all parent paths
necessary to reach the target property.
Parameters:
- *nested_paths: Dot-delimited paths to include with parents
Returns:
PropertyFilter: Function that includes paths and their parents
"""Usage examples:
def test_nested_path_inclusion(snapshot):
from syrupy.filters import paths_include
deep_structure = {
"level1": {
"level2": {
"level3": {
"target_field": "important_data",
"other_field": "not_needed"
},
"sibling": "also_not_needed"
},
"other_branch": "ignore_this"
},
"root_field": "ignore_this_too"
}
# Include only level1.level2.level3.target_field and necessary parents
# This automatically includes: level1, level1.level2, level1.level2.level3
assert deep_structure == snapshot(include=paths_include(
"level1.level2.level3.target_field"
))
def test_multiple_nested_inclusions(snapshot):
from syrupy.filters import paths_include
config = {
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"username": "admin",
"password": "secret"
}
},
"cache": {
"redis": {
"host": "cache-host",
"port": 6379
}
},
"logging": {
"level": "INFO",
"file": "/var/log/app.log"
}
}
# Include multiple nested paths - parents automatically included
assert config == snapshot(include=paths_include(
"database.host",
"database.port",
"cache.redis.host",
"logging.level"
))Using filters together with matchers and extensions for comprehensive snapshot control.
# Filters can be combined with matchers and extensions
PropertyFilter = Callable[[PropertyName, PropertyPath], bool]Usage examples:
def test_filter_with_matcher(snapshot):
from syrupy.filters import props
from syrupy.matchers import path_type
user_activity = {
"user_id": 12345,
"username": "alice",
"last_login": "2023-12-01T10:30:00Z",
"login_count": 42,
"internal_tracking_id": "track_abc123",
"debug_info": {"trace": "internal_data"}
}
# Exclude internal fields AND replace dynamic values
assert user_activity == snapshot(
exclude=props("internal_tracking_id", "debug_info"),
matcher=path_type({
"user_id": (int, "<user_id>"),
"last_login": (str, "<timestamp>")
})
)
def test_filter_with_extension(snapshot):
from syrupy.filters import paths
from syrupy.extensions.json import JSONSnapshotExtension
api_data = {
"public_api": {
"users": [{"id": 1, "name": "Alice"}],
"total": 1
},
"internal_meta": {
"query_time": 0.05,
"cache_hit": True,
"debug_trace": "internal"
}
}
# Include only public API data and save as clean JSON
assert api_data == snapshot(
include=paths("public_api.users", "public_api.total")
).use_extension(JSONSnapshotExtension)Create custom filter functions for specialized filtering needs.
PropertyFilter = Callable[[PropertyName, PropertyPath], bool]
# PropertyPath structure for custom filters
PropertyName = Hashable
PropertyValueType = Type[SerializableData]
PropertyPathEntry = Tuple[PropertyName, PropertyValueType]
PropertyPath = Tuple[PropertyPathEntry, ...]Usage examples:
def test_custom_type_filter(snapshot):
def exclude_private_types(prop_name, path):
"""Exclude properties containing sensitive types"""
# Get the property value type from the path
if path and len(path) > 0:
prop_type = path[-1][1] # Last entry's type
# Exclude certain types
if prop_type in (type(lambda: None), type): # Functions and type objects
return False
# Exclude properties with "private" or "secret" in name
if isinstance(prop_name, str):
if "private" in prop_name.lower() or "secret" in prop_name.lower():
return False
return True # Include by default
test_data = {
"public_value": "visible",
"private_key": "hidden",
"secret_token": "hidden",
"normal_field": "visible",
"callback_func": lambda x: x, # Function type - excluded
"metadata": {"version": "1.0"}
}
assert test_data == snapshot(include=exclude_private_types)
def test_depth_based_filter(snapshot):
def limit_depth(prop_name, path):
"""Limit serialization depth to prevent infinite recursion"""
max_depth = 3
return len(path) <= max_depth
# Deeply nested structure
deep_data = {
"level1": {
"level2": {
"level3": {
"level4": "too deep - excluded",
"value": "included"
},
"value": "included"
},
"value": "included"
}
}
assert deep_data == snapshot(include=limit_depth)
def test_conditional_filter(snapshot):
def filter_by_value_type(prop_name, path):
"""Custom logic based on property name patterns"""
prop_str = str(prop_name)
# Include all numeric IDs
if prop_str.endswith("_id") or prop_str == "id":
return True
# Include all timestamps
if "time" in prop_str.lower() or "date" in prop_str.lower():
return True
# Include names and titles
if prop_str in ["name", "title", "description"]:
return True
# Exclude everything else
return False
mixed_data = {
"user_id": 123,
"name": "Alice",
"email": "alice@example.com", # Excluded
"created_time": "2023-12-01",
"password": "secret", # Excluded
"description": "User account"
}
assert mixed_data == snapshot(include=filter_by_value_type)Install with Tessl CLI
npx tessl i tessl/pypi-syrupyevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10