Skip to content

Commit a658d2f

Browse files
committed
docs: document new unmanaged snapshot values
1 parent c785ab8 commit a658d2f

6 files changed

Lines changed: 380 additions & 136 deletions

File tree

docs/customize_repr.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_enum():
4242

4343
inline-snapshot comes with a special implementation for the following types:
4444

45-
```python exec="1"
45+
``` python exec="1"
4646
from inline_snapshot._code_repr import code_repr_dispatch, code_repr
4747

4848
for name, obj in sorted(
@@ -60,7 +60,7 @@ for name, obj in sorted(
6060

6161
Container types like `dict` or `dataclass` need a special implementation because it is necessary that the implementation uses `repr()` for the child elements.
6262

63-
```python exec="1" result="python"
63+
``` python exec="1" result="python"
6464
print('--8<-- "src/inline_snapshot/_code_repr.py:list"')
6565
```
6666

docs/eq_snapshot.md

Lines changed: 167 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,31 @@ Example:
3333
def test_something():
3434
assert 2 + 40 == snapshot(42)
3535
```
36+
## unmanaged snapshot parts
3637

38+
inline-snapshots manages everything inside `snapshot(...)`, which means that the developer should not change these parts, but there are cases where it is useful to give the developer a bit more control over the snapshot content.
3739

38-
## dirty-equals
40+
Therefor some types will be ignored by inline-snapshot and will **not be updated or fixed**, even if they cause tests to fail.
41+
42+
These types are:
43+
44+
* dirty-equals expression
45+
* dynamic code inside `Is(...)`
46+
* and snapshots inside snapshots.
47+
48+
inline-snapshot is able to handle these types inside the following containers:
49+
50+
* list
51+
* tuple
52+
* dict
53+
* namedtuple
54+
* dataclass
55+
<!--
56+
* pydantic models
57+
* attrs
58+
-->
59+
60+
### dirty-equals
3961

4062
It might be, that larger snapshots with many lists and dictionaries contain some values which change frequently and are not relevant for the test.
4163
They might be part of larger data structures and be difficult to normalize.
@@ -82,7 +104,7 @@ Example:
82104

83105
inline-snapshot tries to change only the values that it needs to change in order to pass the equality comparison.
84106
This allows to replace parts of the snapshot with [dirty-equals](https://dirty-equals.helpmanual.io/latest/) expressions.
85-
This expressions are preserved as long as the `==` comparison with them is `True`.
107+
This expressions are preserved even if the `==` comparison with them is `False`.
86108

87109
Example:
88110

@@ -159,8 +181,149 @@ Example:
159181
)
160182
```
161183

162-
!!! note
163-
The current implementation looks only into lists, dictionaries and tuples and not into the representation of other data structures.
184+
### Is(...)
185+
186+
`Is()` can be used to put runtime values inside snapshots.
187+
It tells inline-snapshot that the developer wants control over some part of the snapshot.
188+
189+
<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
190+
``` python
191+
from inline_snapshot import snapshot, Is
192+
193+
current_version = "1.5"
194+
195+
196+
def request():
197+
return {"data": "page data", "version": current_version}
198+
199+
200+
def test_function():
201+
assert request() == snapshot(
202+
{"data": "page data", "version": Is(current_version)}
203+
)
204+
```
205+
206+
The `current_version` can now be changed without having to correct the snapshot.
207+
208+
`Is()` can also be used when the snapshot is evaluated multiple times.
209+
210+
=== "original code"
211+
<!-- inline-snapshot: first_block outcome-failed=1 -->
212+
``` python
213+
from inline_snapshot import snapshot, Is
214+
215+
216+
def test_function():
217+
for c in "abc":
218+
assert [c, "correct"] == snapshot([Is(c), "wrong"])
219+
```
220+
221+
=== "--inline-snapshot=fix"
222+
<!-- inline-snapshot: fix outcome-passed=1 -->
223+
``` python hl_lines="6"
224+
from inline_snapshot import snapshot, Is
225+
226+
227+
def test_function():
228+
for c in "abc":
229+
assert [c, "correct"] == snapshot([Is(c), "correct"])
230+
```
231+
232+
### inner snapshots
233+
234+
Snapshots can be used inside other snapshots in different use cases.
235+
236+
#### conditional snapshots
237+
It is possible to describe version specific parts of snapshots by replacing the specific part with `#!python snapshot() if some_condition else snapshot()`.
238+
The test has to be executed in each specific condition to fill the snapshots.
239+
240+
The following example shows how this can be used to run a tests with two different library versions:
241+
242+
=== "my_lib v1"
243+
244+
<!-- inline-snapshot-lib: my_lib.py -->
245+
``` python
246+
version = 1
247+
248+
249+
def get_schema():
250+
return [{"name": "var_1", "type": "int"}]
251+
```
252+
253+
=== "my_lib v2"
254+
255+
<!-- inline-snapshot-lib: my_lib.py -->
256+
``` python
257+
version = 2
258+
259+
260+
def get_schema():
261+
return [{"name": "var_1", "type": "string"}]
262+
```
263+
264+
265+
<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
266+
``` python
267+
from inline_snapshot import snapshot
268+
from my_lib import version, get_schema
269+
270+
271+
def test_function():
272+
assert get_schema() == snapshot(
273+
[
274+
{
275+
"name": "var_1",
276+
"type": snapshot("int") if version < 2 else snapshot("string"),
277+
}
278+
]
279+
)
280+
```
281+
282+
The advantage of this approach is that the test uses always the correct values for the test of each library version.
283+
284+
#### common snapshot parts
285+
286+
Another usecase is the extraction of common snapshot parts into an extra snapshot:
287+
288+
<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
289+
``` python
290+
from inline_snapshot import snapshot
291+
292+
293+
def some_data(name):
294+
return {"header": "really long header\n" * 5, "your name": name}
295+
296+
297+
def test_function():
298+
299+
header = snapshot(
300+
"""\
301+
really long header
302+
really long header
303+
really long header
304+
really long header
305+
really long header
306+
"""
307+
)
308+
309+
assert some_data("Tom") == snapshot(
310+
{
311+
"header": header,
312+
"your name": "Tom",
313+
}
314+
)
315+
316+
assert some_data("Bob") == snapshot(
317+
{
318+
"header": header,
319+
"your name": "Bob",
320+
}
321+
)
322+
```
323+
324+
This simplifies test data and allows inline-snapshot to update your values if required.
325+
It makes also sure that the header is the same in both cases.
326+
164327

165328
## pytest options
166329

docs/pytest.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ inline-snapshot provides one pytest option with different flags (*create*,
1111
Snapshot comparisons return always `True` if you use one of the flags *create*, *fix* or *review*.
1212
This is necessary because the whole test needs to be run to fix all snapshots like in this case:
1313

14-
```python
14+
``` python
1515
from inline_snapshot import snapshot
1616

1717

@@ -30,7 +30,7 @@ def test_something():
3030
Approve the changes of the given [category](categories.md).
3131
These flags can be combined with *report* and *review*.
3232

33-
```python title="test_something.py"
33+
``` python title="test_something.py"
3434
from inline_snapshot import snapshot
3535

3636

src/inline_snapshot/_code_repr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def customize_repr(f):
6262
"""Register a funtion which should be used to get the code representation
6363
of a object.
6464
65-
```python
65+
``` python
6666
@customize_repr
6767
def _(obj: MyCustomClass):
6868
return f"MyCustomClass(attr={repr(obj.attr)})"

tests/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ def format(self):
288288
)
289289

290290
def pyproject(self, source):
291-
(pytester.path / "pyproject.toml").write_text(source, "utf-8")
291+
self.write_file("pyproject.toml", source)
292+
293+
def write_file(self, filename, content):
294+
(pytester.path / filename).write_text(content, "utf-8")
292295

293296
def storage(self):
294297
dir = pytester.path / ".inline-snapshot" / "external"

0 commit comments

Comments
 (0)