diff --git a/docs/customize_repr.md b/docs/customize_repr.md index 4bde16c..ae8cc10 100644 --- a/docs/customize_repr.md +++ b/docs/customize_repr.md @@ -3,6 +3,7 @@ `repr()` can be used to convert a python object into a source code representation of the object, but this does not work for every type. Here are some examples: + ```pycon >>> repr(int) "" @@ -58,16 +59,14 @@ for name, obj in sorted( print(f"- `{name}`") ``` -Container types like `dict` or `dataclass` need a special implementation because it is necessary that the implementation uses `repr()` for the child elements. +!!! note + Container types like `dict`, `list`, `tuple` or `dataclass` are handled in a different way, because inline-snapshot also needs to inspect these types to implement [unmanaged](/eq_snapshot.md#unmanaged-snapshot-values) snapshot values. + -``` python exec="1" result="python" -print('--8<-- "src/inline_snapshot/_code_repr.py:list"') -``` -!!! note - using `#!python f"{obj!r}"` or `#!c PyObject_Repr()` will not work, because inline-snapshot replaces `#!python builtins.repr` during the code generation. -## customize + +## customize recursive repr You can also use `repr()` inside `__repr__()`, if you want to make your own type compatible with inline-snapshot. @@ -102,6 +101,13 @@ def test_enum(): assert Pair(E.a, [E.b]) == snapshot(Pair(E.a, [E.b])) ``` +!!! note + using `#!python f"{obj!r}"` or `#!c PyObject_Repr()` will not work, because inline-snapshot replaces `#!python builtins.repr` during the code generation. The only way to use the custom repr implementation is to use the `repr()` function. + +!!! note + This implementation allows inline-snapshot to use the custom `repr()` recursively, but it does not allow you to use [unmanaged](/eq_snapshot.md#unmanaged-snapshot-parts) snapshot values like `#!python Pair(Is(some_var),5)` + + you can also customize the representation of datatypes in other libraries: ``` python diff --git a/docs/eq_snapshot.md b/docs/eq_snapshot.md index be96f1d..907ea31 100644 --- a/docs/eq_snapshot.md +++ b/docs/eq_snapshot.md @@ -33,19 +33,20 @@ Example: def test_something(): assert 2 + 40 == snapshot(42) ``` -## unmanaged snapshot parts +## unmanaged snapshot values -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. +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 the control over the snapshot content back. 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. +* [dirty-equals](#dirty-equals) expressions, +* [dynamic code](#is) inside `Is(...)`, +* [snapshots](#inner-snapshots) inside snapshots and +* [f-strings](#f-strings). -inline-snapshot is able to handle these types inside the following containers: +inline-snapshot is able to handle these types within the following containers: * list * tuple @@ -57,6 +58,8 @@ inline-snapshot is able to handle these types inside the following containers: * attrs --> +Other types are converted with a [customizable](customize_repr.md) `repr()` into code. It is not possible to use unmanaged snapshot values within these objects. + ### 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. @@ -102,9 +105,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 even if the `==` comparison with them is `False`. +The date can be replaced with the [dirty-equals](https://dirty-equals.helpmanual.io/latest/) expressions `IsDatetime()`. Example: @@ -203,9 +204,9 @@ def test_function(): ) ``` -The `current_version` can now be changed without having to correct the snapshot. +The snapshot does not need to be fixed when `current_version` changes in the future, but `"page data"` will still be fixed if it changes. -`Is()` can also be used when the snapshot is evaluated multiple times. +`Is()` can also be used when the snapshot is evaluated multiple times, which is useful in loops or parametrized tests. === "original code" @@ -234,12 +235,14 @@ The `current_version` can now be changed without having to correct the snapshot. 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()`. +It is also possible to use snapshots inside snapshots. + +This is useful 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" +=== "my_lib.py v1" ``` python @@ -250,7 +253,7 @@ The following example shows how this can be used to run a tests with two differe return [{"name": "var_1", "type": "int"}] ``` -=== "my_lib v2" +=== "my_lib.py v2" ``` python @@ -281,9 +284,33 @@ def test_function(): The advantage of this approach is that the test uses always the correct values for each library version. +You can also extract the version logic into its own function. + +``` python +from inline_snapshot import snapshot, Snapshot +from my_lib import version, get_schema + + +def version_snapshot(v1: Snapshot, v2: Snapshot): + return v1 if version < 2 else v2 + + +def test_function(): + assert get_schema() == snapshot( + [ + { + "name": "var_1", + "type": version_snapshot( + v1=snapshot("int"), v2=snapshot("string") + ), + } + ] + ) +``` + #### common snapshot parts -Another usecase is the extraction of common snapshot parts into an extra snapshot: +Another use case is the extraction of common snapshot parts into an extra snapshot: ``` python @@ -346,12 +373,12 @@ def test_get_error(): It is not required to wrap the changed value in `Is(f"...")`, because inline-snapshot knows that *f-strings* are only generated by the developer. !!! Warning "Limitation" - inline-snapshot is currently not able to fix the string constants inside *f-strings*. - This feature is planned for a future version. - The formatted values within the *f-string* (`{__file__}` in the example above) will be treated as unmanaged snapshot values. - `Is()` will not be required in this case. + inline-snapshot is currently not able to fix the string constants within *f-strings*. + + `#!python f"...{var}..."` works **currently** like `#!python Is(f"...{var}...")` and issues a warning if the value changes, giving you the opportunity to fix your f-string. + + `#!python f"...{var}..."` will in the **future** work like `#!python f"...{Is(var)}"`. inline-snapshot will then be able to *fix* the string parts within the f-string. - The current implementation shows a warning which contains the new string value if the value is different to the current one. ## pytest options diff --git a/mkdocs.yml b/mkdocs.yml index 82efbc3..04fe641 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,11 @@ theme: media: '(prefers-color-scheme: dark)' primary: teal +validation: + links: + absolute_links: relative_to_docs + + watch: - CONTRIBUTING.md - CHANGELOG.md