|
| 1 | +Storing snapshots in the source code is the main feature of inline snapshots. |
| 2 | +This has the advantage that you can easily see changes in code reviews. However, it also has some drawbacks: |
| 3 | + |
| 4 | +* It is problematic to snapshot a large amount of data, as it consumes significant space in your tests. |
| 5 | +* Binary data or images are not human-readable in your tests. |
| 6 | + |
| 7 | +`external()` solves this problem and integrates nicely with inline snapshots. |
| 8 | +It stores a reference to the external data in a special `external()` object, which can be used like `snapshot()`. |
| 9 | + |
| 10 | +There are different storage protocols, such as [*hash*](#hash) or [*uuid*](#uuid), and different file formats, such as *.txt*, *.bin*, and *.json*. It is also possible to implement [*custom*](register_format.md) file formats. |
| 11 | + |
| 12 | + |
| 13 | +Example: |
| 14 | + |
| 15 | +<!-- inline-snapshot: first_block outcome-failed=1 outcome-errors=1 --> |
| 16 | +``` python |
| 17 | +from inline_snapshot import external |
| 18 | + |
| 19 | + |
| 20 | +def test_something(): |
| 21 | + # inline-snapshot can determine the correct file types |
| 22 | + assert "string" == external() |
| 23 | + assert b"bytes" == external() |
| 24 | + |
| 25 | + # Data structures with lists and dictionaries are stored as JSON |
| 26 | + assert ["json", "like", "data"] == external() |
| 27 | + |
| 28 | + # You can also explicitly specify the storage protocol |
| 29 | + assert "other text" == external("uuid:") |
| 30 | + |
| 31 | + # And the format (.json instead of the default .txt in this case) |
| 32 | + assert "other text" == external("uuid:.json") |
| 33 | +``` |
| 34 | + |
| 35 | +inline-snapshot will then fill in the missing parts when you create your snapshots. It will keep your specified protocols and file types and generate names for your snapshots. |
| 36 | + |
| 37 | +<!-- inline-snapshot: create outcome-passed=1 outcome-errors=1 --> |
| 38 | +``` python hl_lines="6 7 10 11 12 15 16 17 20 21 22" |
| 39 | +from inline_snapshot import external |
| 40 | + |
| 41 | + |
| 42 | +def test_something(): |
| 43 | + # inline-snapshot can determine the correct file types |
| 44 | + assert "string" == external("uuid:e3e70682-c209-4cac-a29f-6fbed82c07cd.txt") |
| 45 | + assert b"bytes" == external("uuid:f728b4fa-4248-4e3a-8a5d-2f346baa9455.bin") |
| 46 | + |
| 47 | + # Data structures with lists and dictionaries are stored as JSON |
| 48 | + assert ["json", "like", "data"] == external( |
| 49 | + "uuid:eb1167b3-67a9-4378-bc65-c1e582e2e662.json" |
| 50 | + ) |
| 51 | + |
| 52 | + # You can also explicitly specify the storage protocol |
| 53 | + assert "other text" == external( |
| 54 | + "uuid:f7c1bd87-4da5-4709-9471-3d60c8a70639.txt" |
| 55 | + ) |
| 56 | + |
| 57 | + # And the format (.json instead of the default .txt in this case) |
| 58 | + assert "other text" == external( |
| 59 | + "uuid:e443df78-9558-467f-9ba9-1faf7a024204.json" |
| 60 | + ) |
| 61 | +``` |
| 62 | + |
| 63 | +The `external()` function can also be used inside other data structures. |
| 64 | + |
| 65 | +<!-- inline-snapshot: first_block outcome-failed=1 outcome-errors=1 --> |
| 66 | +``` python |
| 67 | +from inline_snapshot import snapshot, external |
| 68 | + |
| 69 | + |
| 70 | +def test_something(): |
| 71 | + assert ["long text\n" * times for times in [1, 2, 1000]] == snapshot( |
| 72 | + [..., ..., external()] |
| 73 | + ) |
| 74 | +``` |
| 75 | + |
| 76 | +<!-- inline-snapshot: create fix outcome-passed=1 outcome-errors=1 --> |
| 77 | +``` python hl_lines="6 7 8 9 10 11 12 13" |
| 78 | +from inline_snapshot import snapshot, external |
| 79 | + |
| 80 | + |
| 81 | +def test_something(): |
| 82 | + assert ["long text\n" * times for times in [1, 2, 1000]] == snapshot( |
| 83 | + [ |
| 84 | + "long text\n", |
| 85 | + """\ |
| 86 | +long text |
| 87 | +long text |
| 88 | +""", |
| 89 | + external("uuid:e3e70682-c209-4cac-a29f-6fbed82c07cd.txt"), |
| 90 | + ] |
| 91 | + ) |
| 92 | +``` |
| 93 | + |
| 94 | +## Storage Protocols |
| 95 | + |
| 96 | +### UUID |
| 97 | + |
| 98 | +The `uuid:` storage protocol is the default protocol and stores the external files relative to the test files in `__inline_snapshot__/<test_file>/<qualname>/<uuid>.suffix`. |
| 99 | + |
| 100 | +- :material-plus:{.green} Files are co-located with the file/function where your value is used. |
| 101 | +- :material-plus:{.green} The use of a UUID allows inline-snapshot to find the external file even if file or function names of a test function have changed. |
| 102 | +- :material-minus:{.red} Distinguishing multiple external snapshots in the same function remains challenging. |
| 103 | + |
| 104 | +### Hash |
| 105 | + |
| 106 | +The `hash:` storage can be used to store snapshot files based on the hash of their content. This was the first storage protocol supported by inline-snapshot and can still be useful in some cases. It also preserves backward compatibility with older inline-snapshot versions. |
| 107 | + |
| 108 | +- :material-plus:{.green} Value changes cause source code changes because the hash changes. |
| 109 | +- :material-minus:{.red} GitHub/GitLab web UIs cannot be used to view the diffs, because the filename changes. |
| 110 | + |
| 111 | +## Formats |
| 112 | + |
| 113 | +inline-snapshot supports several built-in formats for external snapshots. The format used is determined by the given data type: bytes are stored in a `.bin` file, and strings are stored in a `.txt` file by default. More complex data types are stored in a `.json` file. |
| 114 | + |
| 115 | +<!--[[[cog |
| 116 | +from inline_snapshot._global_state import state |
| 117 | +import cog |
| 118 | +
|
| 119 | +cog.out("|Suffix|Priority|Description|\n") |
| 120 | +cog.out("|---|---|---|\n") |
| 121 | +for format in sorted(state().all_formats.values(),key=lambda f:-f.priority): |
| 122 | + cog.out(f"| `{format.suffix}` | {format.priority}| {format.__doc__}|\n") |
| 123 | +
|
| 124 | +]]]--> |
| 125 | +|Suffix|Priority|Description| |
| 126 | +|---|---|---| |
| 127 | +| `.bin` | 0| Stores bytes in `.bin` files and shows them as a hexdump.| |
| 128 | +| `.txt` | 0| Stores strings in `.txt` files.| |
| 129 | +| `.json` | -10| Stores the data with `json.dump()`.| |
| 130 | +<!--[[[end]]]--> |
| 131 | + |
| 132 | +[Custom formats](register_format.md) are also supported. |
| 133 | + |
| 134 | +You can also use format aliases if you want to use specific file suffixes that have the same handling as existing formats. |
| 135 | +You must specify the suffix in this case. |
| 136 | + |
| 137 | +<!-- inline-snapshot: first_block outcome-failed=1 outcome-errors=1 --> |
| 138 | +``` python |
| 139 | +from inline_snapshot import register_format_alias, external |
| 140 | + |
| 141 | +register_format_alias(".html", ".txt") |
| 142 | + |
| 143 | + |
| 144 | +def test(): |
| 145 | + assert "<html></html>" == external(".html") |
| 146 | +``` |
| 147 | + |
| 148 | +inline-snapshot uses the given suffix to create an external snapshot. |
| 149 | + |
| 150 | +<!-- inline-snapshot: create outcome-passed=1 outcome-errors=1 --> |
| 151 | +``` python hl_lines="7 8 9" |
| 152 | +from inline_snapshot import register_format_alias, external |
| 153 | + |
| 154 | +register_format_alias(".html", ".txt") |
| 155 | + |
| 156 | + |
| 157 | +def test(): |
| 158 | + assert "<html></html>" == external( |
| 159 | + "uuid:e3e70682-c209-4cac-a29f-6fbed82c07cd.html" |
| 160 | + ) |
| 161 | +``` |
| 162 | + |
| 163 | +!!! important "Breaking Change" |
| 164 | + `register_format_alias()` is required if you used `outsource(value, suffix="html")` and are migrating from inline-snapshot prior to version 0.24. |
| 165 | + |
| 166 | +## pytest Options |
| 167 | + |
| 168 | +It interacts with the following `--inline-snapshot` flags: |
| 169 | + |
| 170 | +- `create`: Creates new external files. |
| 171 | +- `fix`: Changes external files. |
| 172 | +- `trim`: Removes all snapshots from the storage that are not referenced with `external(...)` in the code. |
0 commit comments