Skip to content

Commit d283e87

Browse files
authored
feat: add option to disable diffing (#924)
For extremely large snapshot files, the diff algorithm is not very efficient. Until the algorithm can be modified to work with large files, there is now a --snapshot-diff-mode=disabled flag that can be specified to disable diffing on snapshot assertion failures.
1 parent 8c1208d commit d283e87

File tree

6 files changed

+95
-8
lines changed

6 files changed

+95
-8
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ These are the cli options exposed to `pytest` by the plugin.
139139
| `--snapshot-default-extension` | Use to change the default snapshot extension class. | [AmberSnapshotExtension](https://github.com/syrupy-project/syrupy/blob/main/src/syrupy/extensions/amber/__init__.py) |
140140
| `--snapshot-no-colors` | Disable test results output highlighting. Equivalent to setting the environment variables `ANSI_COLORS_DISABLED` or `NO_COLOR` | Disabled by default if not in terminal. |
141141
| `--snapshot-patch-pycharm-diff`| Override PyCharm's default diffs viewer when looking at snapshot diffs. See [IDE Integrations](#ide-integrations) | `False` |
142+
| `--snapshot-diff-mode` | Configures how diffs are displayed on assertion failure. If working with very large snapshots, disabling the diff can improve performance. | `detailed` |
142143

143144
### Assertion Options
144145

src/syrupy/__init__.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import pytest
1414

15-
from .assertion import SnapshotAssertion
15+
from .assertion import DiffMode, SnapshotAssertion
1616
from .constants import DISABLE_COLOR_ENV_VAR
1717
from .exceptions import FailedToLoadModuleMember
1818
from .extensions import DEFAULT_EXTENSION
@@ -43,7 +43,7 @@ def __import_extension(value: Optional[str]) -> Any:
4343
raise argparse.ArgumentTypeError(e) from e
4444

4545

46-
def pytest_addoption(parser: Any) -> None:
46+
def pytest_addoption(parser: "pytest.Parser") -> None:
4747
"""
4848
Exposes snapshot plugin configuration to pytest.
4949
https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_addoption
@@ -94,9 +94,17 @@ def pytest_addoption(parser: Any) -> None:
9494
dest="patch_pycharm_diff",
9595
help="Patch PyCharm diff",
9696
)
97+
group.addoption(
98+
"--snapshot-diff-mode",
99+
default=DiffMode.DETAILED,
100+
choices=list(DiffMode),
101+
type=DiffMode,
102+
dest="diff_mode",
103+
help="Controls how diffs are represented on snapshot assertion failure",
104+
)
97105

98106

99-
def __terminal_color(config: Any) -> "ContextManager[None]":
107+
def __terminal_color(config: "pytest.Config") -> "ContextManager[None]":
100108
env = {}
101109
if config.option.no_colors:
102110
env[DISABLE_COLOR_ENV_VAR] = "true"
@@ -105,7 +113,7 @@ def __terminal_color(config: Any) -> "ContextManager[None]":
105113

106114

107115
def pytest_assertrepr_compare(
108-
config: Any, op: str, left: Any, right: Any
116+
config: "pytest.Config", op: str, left: Any, right: Any
109117
) -> Optional[List[str]]:
110118
"""
111119
Return explanation for comparisons in failing assert expressions.
@@ -119,10 +127,14 @@ def snapshot_name(name: str) -> str:
119127

120128
if isinstance(left, SnapshotAssertion):
121129
assert_msg = reset(f"{snapshot_name(left.name)} {op} {received_name}")
122-
return [assert_msg] + left.get_assert_diff()
130+
return [assert_msg] + left.get_assert_diff(
131+
diff_mode=config.option.diff_mode
132+
)
123133
elif isinstance(right, SnapshotAssertion):
124134
assert_msg = reset(f"{received_name} {op} {snapshot_name(right.name)}")
125-
return [assert_msg] + right.get_assert_diff()
135+
return [assert_msg] + right.get_assert_diff(
136+
diff_mode=config.option.diff_mode
137+
)
126138
return None
127139

128140

src/syrupy/assertion.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
dataclass,
55
field,
66
)
7+
from enum import Enum
78
from gettext import gettext
89
from typing import (
910
TYPE_CHECKING,
@@ -35,6 +36,14 @@
3536
)
3637

3738

39+
class DiffMode(Enum):
40+
DETAILED = "detailed"
41+
DISABLED = "disabled"
42+
43+
def __str__(self) -> str:
44+
return self.value
45+
46+
3847
@dataclass
3948
class AssertionResult:
4049
snapshot_location: str
@@ -210,7 +219,9 @@ def _serialize(self, data: "SerializableData") -> "SerializedData":
210219
data, exclude=self._exclude, include=self._include, matcher=self.__matcher
211220
)
212221

213-
def get_assert_diff(self) -> List[str]:
222+
def get_assert_diff(
223+
self, *, diff_mode: "DiffMode" = DiffMode.DETAILED
224+
) -> List[str]:
214225
assertion_result = self._execution_results[self.num_executions - 1]
215226
if assertion_result.exception:
216227
if isinstance(assertion_result.exception, (TaintedSnapshotError,)):
@@ -247,7 +258,8 @@ def get_assert_diff(self) -> List[str]:
247258
)
248259
if not assertion_result.success:
249260
snapshot_data = snapshot_data if snapshot_data is not None else ""
250-
diff.extend(self.extension.diff_lines(serialized_data, snapshot_data))
261+
if diff_mode == DiffMode.DETAILED:
262+
diff.extend(self.extension.diff_lines(serialized_data, snapshot_data))
251263
return diff
252264

253265
def __with_prop(self, prop_name: str, prop_value: Any) -> None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
3+
4+
@pytest.fixture
5+
def testfile(testdir) -> pytest.Testdir:
6+
testdir.makepyfile(
7+
test_file=(
8+
"""
9+
def test_case(snapshot):
10+
assert snapshot == "some-value"
11+
"""
12+
),
13+
)
14+
return testdir
15+
16+
17+
def test_diff_mode_disabled_does_not_print_diff(
18+
testfile,
19+
):
20+
# Generate initial snapshot
21+
result = testfile.runpytest("-v", "--snapshot-update")
22+
result.stdout.re_match_lines((r"1 snapshot generated\.",))
23+
assert result.ret == 0
24+
25+
# Modify snapshot to generate diff
26+
testfile.makepyfile(
27+
test_file=(
28+
"""
29+
def test_case(snapshot):
30+
assert snapshot == "some-other-value"
31+
"""
32+
),
33+
)
34+
35+
# With diff we expect to see "some-other-value"
36+
result = testfile.runpytest("-v", "--snapshot-diff-mode=detailed")
37+
result.stdout.re_match_lines(
38+
(
39+
r".*- 'some-value'",
40+
r".*\+ 'some-other-value'",
41+
)
42+
)
43+
assert result.ret == 1
44+
45+
# Without diff we do not expect to see "some-other-value"
46+
result = testfile.runpytest("-v", "--snapshot-diff-mode=disabled")
47+
result.stdout.no_re_match_line(r".*- 'some-value'")
48+
result.stdout.no_re_match_line(r".*\+ 'some-other-value'")
49+
assert result.ret == 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# serializer version: 1
2+
# name: test_can_be_stringified
3+
'detailed'
4+
# ---
5+
# name: test_can_be_stringified.1
6+
'disabled'
7+
# ---

tests/syrupy/test_diff_mode.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from syrupy.assertion import DiffMode
2+
3+
4+
def test_can_be_stringified(snapshot):
5+
assert snapshot == str(DiffMode.DETAILED)
6+
assert snapshot == str(DiffMode.DISABLED)

0 commit comments

Comments
 (0)