Skip to content

Commit

Permalink
docs: document new unmanaged snapshot values
Browse files Browse the repository at this point in the history
  • Loading branch information
15r10nk committed Oct 19, 2024
1 parent c785ab8 commit 1cb18a7
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 136 deletions.
4 changes: 2 additions & 2 deletions docs/customize_repr.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_enum():

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

```python exec="1"
``` python exec="1"
from inline_snapshot._code_repr import code_repr_dispatch, code_repr

for name, obj in sorted(
Expand All @@ -60,7 +60,7 @@ for name, obj in sorted(

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

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

Expand Down
171 changes: 167 additions & 4 deletions docs/eq_snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,31 @@ Example:
def test_something():
assert 2 + 40 == snapshot(42)
```
## unmanaged snapshot parts

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.

## dirty-equals
Therefor some types will be ignored by inline-snapshot and will **not be updated or fixed**, even if they cause tests to fail.

These types are:

* dirty-equals expression
* dynamic code inside `Is(...)`
* and snapshots inside snapshots.

inline-snapshot is able to handle these types inside the following containers:

* list
* tuple
* dict
* namedtuple
* dataclass
<!--
* pydantic models
* attrs
-->

### dirty-equals

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

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

Example:

Expand Down Expand Up @@ -159,8 +181,149 @@ Example:
)
```

!!! note
The current implementation looks only into lists, dictionaries and tuples and not into the representation of other data structures.
### Is(...)

`Is()` can be used to put runtime values inside snapshots.
It tells inline-snapshot that the developer wants control over some part of the snapshot.

<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
``` python
from inline_snapshot import snapshot, Is

current_version = "1.5"


def request():
return {"data": "page data", "version": current_version}


def test_function():
assert request() == snapshot(
{"data": "page data", "version": Is(current_version)}
)
```

The `current_version` can now be changed without having to correct the snapshot.

`Is()` can also be used when the snapshot is evaluated multiple times.

=== "original code"
<!-- inline-snapshot: first_block outcome-failed=1 -->
``` python
from inline_snapshot import snapshot, Is


def test_function():
for c in "abc":
assert [c, "correct"] == snapshot([Is(c), "wrong"])
```

=== "--inline-snapshot=fix"
<!-- inline-snapshot: fix outcome-passed=1 -->
``` python hl_lines="6"
from inline_snapshot import snapshot, Is


def test_function():
for c in "abc":
assert [c, "correct"] == snapshot([Is(c), "correct"])
```

### inner snapshots

Snapshots can be used inside other snapshots in different use cases.

#### conditional snapshots
It is possible to describe version specific parts of snapshots by replacing the specific part with `#!python snapshot() if some_condition else snapshot()`.
The test has to be executed in each specific condition to fill the snapshots.

The following example shows how this can be used to run a tests with two different library versions:

=== "my_lib v1"

<!-- inline-snapshot-lib: my_lib.py -->
``` python
version = 1


def get_schema():
return [{"name": "var_1", "type": "int"}]
```

=== "my_lib v2"

<!-- inline-snapshot-lib: my_lib.py -->
``` python
version = 2


def get_schema():
return [{"name": "var_1", "type": "string"}]
```


<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
``` python
from inline_snapshot import snapshot
from my_lib import version, get_schema


def test_function():
assert get_schema() == snapshot(
[
{
"name": "var_1",
"type": snapshot("int") if version < 2 else snapshot("string"),
}
]
)
```

The advantage of this approach is that the test uses always the correct values for each library version.

#### common snapshot parts

Another usecase is the extraction of common snapshot parts into an extra snapshot:

<!-- inline-snapshot: create fix first_block outcome-passed=1 -->
``` python
from inline_snapshot import snapshot


def some_data(name):
return {"header": "really long header\n" * 5, "your name": name}


def test_function():

header = snapshot(
"""\
really long header
really long header
really long header
really long header
really long header
"""
)

assert some_data("Tom") == snapshot(
{
"header": header,
"your name": "Tom",
}
)

assert some_data("Bob") == snapshot(
{
"header": header,
"your name": "Bob",
}
)
```

This simplifies test data and allows inline-snapshot to update your values if required.
It makes also sure that the header is the same in both cases.


## pytest options

Expand Down
4 changes: 2 additions & 2 deletions docs/pytest.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ inline-snapshot provides one pytest option with different flags (*create*,
Snapshot comparisons return always `True` if you use one of the flags *create*, *fix* or *review*.
This is necessary because the whole test needs to be run to fix all snapshots like in this case:

```python
``` python
from inline_snapshot import snapshot


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

```python title="test_something.py"
``` python title="test_something.py"
from inline_snapshot import snapshot


Expand Down
2 changes: 1 addition & 1 deletion src/inline_snapshot/_code_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def customize_repr(f):
"""Register a funtion which should be used to get the code representation
of a object.
```python
``` python
@customize_repr
def _(obj: MyCustomClass):
return f"MyCustomClass(attr={repr(obj.attr)})"
Expand Down
5 changes: 4 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,10 @@ def format(self):
)

def pyproject(self, source):
(pytester.path / "pyproject.toml").write_text(source, "utf-8")
self.write_file("pyproject.toml", source)

def write_file(self, filename, content):
(pytester.path / filename).write_text(content, "utf-8")

def storage(self):
dir = pytester.path / ".inline-snapshot" / "external"
Expand Down
Loading

0 comments on commit 1cb18a7

Please sign in to comment.