1
- About
2
- -----
3
-
4
1
This is a plugin to facilitate image comparison for
5
2
`Matplotlib <http://www.matplotlib.org >`__ figures in pytest.
6
3
@@ -9,361 +6,3 @@ generated image, and the RMS of the residual is compared to a
9
6
user-specified tolerance. If the residual is too large, the test will
10
7
fail (this is implemented using helper functions from
11
8
``matplotlib.testing ``).
12
-
13
- For more information on how to write tests to do this, see the **Using **
14
- section below.
15
-
16
- Installing
17
- ----------
18
-
19
- This plugin is compatible with Python 3.6 and later, and
20
- requires `pytest <http://pytest.org >`__ and
21
- `matplotlib <http://www.matplotlib.org >`__ to be installed.
22
-
23
- To install, you can do::
24
-
25
- pip install pytest-mpl
26
-
27
- You can check that the plugin is registered with pytest by doing::
28
-
29
- pytest --version
30
-
31
- which will show a list of plugins:
32
-
33
- ::
34
-
35
- This is pytest version 2.7.1, imported from ...
36
- setuptools registered plugins:
37
- pytest-mpl-0.1 at ...
38
-
39
- Using
40
- -----
41
-
42
- With Baseline Images
43
- ^^^^^^^^^^^^^^^^^^^^
44
-
45
- To use, you simply need to mark the function where you want to compare
46
- images using ``@pytest.mark.mpl_image_compare ``, and make sure that the
47
- function returns a Matplotlib figure (or any figure object that has a
48
- ``savefig `` method):
49
-
50
- .. code :: python
51
-
52
- import pytest
53
- import matplotlib.pyplot as plt
54
-
55
- @pytest.mark.mpl_image_compare
56
- def test_succeeds ():
57
- fig = plt.figure()
58
- ax = fig.add_subplot(1 ,1 ,1 )
59
- ax.plot([1 ,2 ,3 ])
60
- return fig
61
-
62
- To generate the baseline images, run the tests with the
63
- ``--mpl-generate-path `` option with the name of the directory where the
64
- generated images should be placed::
65
-
66
- pytest --mpl-generate-path=baseline
67
-
68
- If the directory does not exist, it will be created. The directory will
69
- be interpreted as being relative to where you are running ``pytest ``.
70
- Once you are happy with the generated images, you should move them to a
71
- sub-directory called ``baseline `` relative to the test files (this name
72
- is configurable, see below). You can also generate the baseline image
73
- directly in the right directory.
74
-
75
- With a Hash Library
76
- ^^^^^^^^^^^^^^^^^^^
77
-
78
- Instead of comparing to baseline images, you can instead compare against a JSON
79
- library of SHA-256 hashes. This has the advantage of not having to check baseline
80
- images into the repository with the tests, or download them from a remote
81
- source.
82
-
83
- The hash library can be generated with
84
- ``--mpl-generate-hash-library=path_to_file.json ``. The hash library to be used
85
- can either be specified via the ``--mpl-hash-library= `` command line argument,
86
- or via the ``hash_library= `` keyword argument to the
87
- ``@pytest.mark.mpl_image_compare `` decorator.
88
-
89
- When generating a hash library, the tests will also be run as usual against the
90
- existing hash library specified by ``--mpl-hash-library `` or the keyword argument.
91
- However, generating baseline images will always result in the tests being skipped.
92
-
93
- Hybrid Mode: Hashes and Images
94
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
95
-
96
- It is possible to configure both hashes and baseline images. In this scenario
97
- only the hash comparison can determine the test result. If the hash comparison
98
- fails, the test will fail, however a comparison to the baseline image will be
99
- carried out so the actual difference can be seen. If the hash comparison passes,
100
- the comparison to the baseline image is skipped (unless **results always ** is
101
- configured).
102
-
103
- This is especially useful if the baseline images are external to the repository
104
- containing the tests, and are accessed via HTTP. In this situation, if the hashes
105
- match, the baseline images won't be retrieved, saving time and bandwidth. Also, it
106
- allows the tests to be modified and the hashes updated to reflect the changes
107
- without having to modify the external images.
108
-
109
-
110
- Running Tests
111
- ^^^^^^^^^^^^^
112
-
113
- Once tests are written with baseline images, a hash library, or both to compare
114
- against, the tests can be run with::
115
-
116
- pytest --mpl
117
-
118
- and the tests will pass if the images are the same. If you omit the
119
- ``--mpl `` option, the tests will run but will only check that the code
120
- runs, without checking the output images.
121
-
122
- If pytest-mpl is not installed, the image comparison tests will cause pytest
123
- to show a warning, ``PytestReturnNotNoneWarning ``. Installing pytest-mpl will
124
- solve this issue. Alternativly, the image comparison tests can be deselected
125
- by running pytest with ``-m "not mpl_image_compare" ``.
126
-
127
-
128
- Generating a Test Summary
129
- ^^^^^^^^^^^^^^^^^^^^^^^^^
130
-
131
- By specifying the ``--mpl-generate-summary=html `` CLI argument, a HTML summary
132
- page will be generated showing the test result, log entry and generated result
133
- image. When in the (default) image comparison mode, the baseline image, diff
134
- image and RMS (if any), and tolerance of each test will also be shown.
135
- When in the hash comparison mode, the baseline hash and result hash will
136
- also be shown. When in hybrid mode, all of these are included.
137
-
138
- When generating a HTML summary, the ``--mpl-results-always `` option is
139
- automatically applied (see section below). Therefore images for passing
140
- tests will also be shown.
141
-
142
- +---------------+---------------+---------------+
143
- | |html all | | |html filter | | |html result | |
144
- +---------------+---------------+---------------+
145
-
146
- As well as ``html ``, ``basic-html `` can be specified for an alternative HTML
147
- summary which does not rely on JavaScript or external resources. A ``json ``
148
- summary can also be saved. Multiple options can be specified comma-separated.
149
-
150
- Options
151
- -------
152
-
153
- Tolerance
154
- ^^^^^^^^^
155
-
156
- The RMS tolerance for the image comparison (which defaults to 2) can be
157
- specified in the ``mpl_image_compare `` decorator with the ``tolerance ``
158
- argument:
159
-
160
- .. code :: python
161
-
162
- @pytest.mark.mpl_image_compare (tolerance = 20 )
163
- def test_image ():
164
- ...
165
-
166
- Savefig options
167
- ^^^^^^^^^^^^^^^
168
-
169
- You can pass keyword arguments to ``savefig `` by using
170
- ``savefig_kwargs `` in the ``mpl_image_compare `` decorator:
171
-
172
- .. code :: python
173
-
174
- @pytest.mark.mpl_image_compare (savefig_kwargs = {' dpi' :300 })
175
- def test_image ():
176
- ...
177
-
178
- Baseline images
179
- ^^^^^^^^^^^^^^^
180
-
181
- The baseline directory (which defaults to ``baseline `` ) and the
182
- filename of the plot (which defaults to the name of the test with a
183
- ``.png `` suffix) can be customized with the ``baseline_dir `` and
184
- ``filename `` arguments in the ``mpl_image_compare `` decorator:
185
-
186
- .. code :: python
187
-
188
- @pytest.mark.mpl_image_compare (baseline_dir = ' baseline_images' ,
189
- filename = ' other_name.png' )
190
- def test_image ():
191
- ...
192
-
193
- The baseline directory in the decorator above will be interpreted as
194
- being relative to the test file. Note that the baseline directory can
195
- also be a URL (which should start with ``http:// `` or ``https:// `` and
196
- end in a slash). If you want to specify mirrors, set ``baseline_dir `` to
197
- a comma-separated list of URLs (real commas in the URL should be encoded
198
- as ``%2C ``).
199
-
200
- Finally, you can also set a custom baseline directory globally when
201
- running tests by running ``pytest `` with::
202
-
203
- pytest --mpl --mpl-baseline-path=baseline_images
204
-
205
- This directory will be interpreted as being relative to where pytest
206
- is run. However, if the ``--mpl-baseline-relative `` option is also
207
- included, this directory will be interpreted as being relative to
208
- the current test directory.
209
- In addition, if both this option and the ``baseline_dir ``
210
- option in the ``mpl_image_compare `` decorator are used, the one in the
211
- decorator takes precedence.
212
-
213
- Results always
214
- ^^^^^^^^^^^^^^
215
-
216
- By default, result images are only saved for tests that fail.
217
- Passing ``--mpl-results-always `` to pytest will force result images
218
- to be saved for all tests, even for tests that pass.
219
-
220
- When in **hybrid mode **, even if a test passes hash comparison,
221
- a comparison to the baseline image will also be carried out,
222
- with the baseline image and diff image (if image comparison fails)
223
- saved for all tests. This secondary comparison will not affect
224
- the success status of the test.
225
-
226
- This option is useful for always *comparing * the result images against
227
- the baseline images, while only *assessing * the tests against the
228
- hash library.
229
- If you only update your baseline images after merging a PR, this
230
- option means that the generated summary will always show how the
231
- PR affects the baseline images, with the success status of each
232
- test (based on the hash library) also shown in the generated
233
- summary. This option is applied automatically when generating
234
- a HTML summary.
235
-
236
- When the ``--mpl-results-always `` option is active, and some hash
237
- comparison tests are performed, a hash library containing all the
238
- result hashes will also be saved to the root of the results directory.
239
- The filename will be extracted from ``--mpl-generate-hash-library ``,
240
- ``--mpl-hash-library `` or ``hash_library= `` in that order.
241
-
242
- Base style
243
- ^^^^^^^^^^
244
-
245
- By default, tests will be run using the Matplotlib 'classic' style
246
- (ignoring any locally defined RC parameters). This can be overridden by
247
- using the ``style `` argument:
248
-
249
- .. code :: python
250
-
251
- @pytest.mark.mpl_image_compare (style = ' fivethirtyeight' )
252
- def test_image ():
253
- ...
254
-
255
- Package version dependencies
256
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
257
- Different versions of Matplotlib and FreeType may result in slightly
258
- different images. When testing on multiple platforms or as part of a
259
- pipeline, it is important to ensure that the versions of these
260
- packages match the versions used to generate the images used for
261
- comparison. It can be useful to pin versions of Matplotlib and FreeType
262
- so as to avoid automatic updates that fail tests.
263
-
264
- Removing text
265
- ^^^^^^^^^^^^^
266
-
267
- If you are running a test for which you are not interested in comparing
268
- the text labels, you can use the ``remove_text `` argument to the
269
- decorator:
270
-
271
- .. code :: python
272
-
273
- @pytest.mark.mpl_image_compare (remove_text = True )
274
- def test_image ():
275
- ...
276
-
277
- This will make the test insensitive to changes in e.g. the freetype
278
- library.
279
-
280
- Supported formats and deterministic output
281
- ------------------------------------------
282
-
283
- By default, pytest-mpl will save and compare figures in PNG format. However,
284
- it is possible to set the format to use by setting e.g. ``savefig_kwargs={'format': 'pdf'} ``
285
- in ``mpl_image_compare ``. Supported formats are ``'eps' ``, ``'pdf' ``, ``'png' ``, and ``'svg' ``.
286
- Note that Ghostscript is required to be installed for comparing PDF and EPS figures, while
287
- Inkscape is required for SVG comparison.
288
-
289
- By default, Matplotlib does not produce deterministic output that will have a
290
- consistent hash every time it is run, or over different Matplotlib versions. In
291
- order to enforce that the output is deterministic, you can set the ``deterministic ``
292
- keyword argument in ``mpl_image_compare ``:
293
-
294
- .. code :: python
295
-
296
- @pytest.mark.mpl_image_compare (deterministic = True )
297
-
298
- This does a number of things such as e.g., setting the creation date in the
299
- metadata to be constant, and avoids hard-coding the Matplotlib in the files.
300
-
301
- Test failure example
302
- --------------------
303
-
304
- If the images produced by the tests are correct, then the test will
305
- pass, but if they are not, the test will fail with a message similar to
306
- the following::
307
-
308
- E Exception: Error: Image files did not match.
309
- E RMS Value: 142.2287807767823
310
- E Expected:
311
- E /var/folders/zy/t1l3sx310d3d6p0kyxqzlrnr0000gr/T/tmp4h4oxr7y/baseline-coords_overlay_auto_coord_meta.png
312
- E Actual:
313
- E /var/folders/zy/t1l3sx310d3d6p0kyxqzlrnr0000gr/T/tmp4h4oxr7y/coords_overlay_auto_coord_meta.png
314
- E Difference:
315
- E /var/folders/zy/t1l3sx310d3d6p0kyxqzlrnr0000gr/T/tmp4h4oxr7y/coords_overlay_auto_coord_meta-failed-diff.png
316
- E Tolerance:
317
- E 10
318
-
319
- The image paths included in the exception are then available for
320
- inspection:
321
-
322
- +----------------+----------------+-------------+
323
- | Expected | Actual | Difference |
324
- +================+================+=============+
325
- | |expected | | |actual | | |diff | |
326
- +----------------+----------------+-------------+
327
-
328
- In this case, the differences are very clear, while in some cases it may
329
- be necessary to use the difference image, or blink the expected and
330
- actual images, in order to see what changed.
331
-
332
- The default tolerance is 2, which is very strict. In some cases, you may
333
- want to relax this to account for differences in fonts across different
334
- systems.
335
-
336
- By default, the expected, actual and difference files are written to a
337
- temporary directory with a non-deterministic path. If you want to instead
338
- write them to a specific directory, you can use::
339
-
340
- pytest --mpl --mpl-results-path=results
341
-
342
- The ``results `` directory will then contain one sub-directory per test, and each
343
- sub-directory will contain the three files mentioned above. If you are using a
344
- continuous integration service, you can then use the option to upload artifacts
345
- to upload these results to somewhere where you can view them. For more
346
- information, see:
347
-
348
- * `Uploading artifacts on Travis-CI <https://docs.travis-ci.com/user/uploading-artifacts/ >`_
349
- * `Build Artifacts (CircleCI) <https://circleci.com/docs/1.0/build-artifacts/ >`_
350
- * `Packaging Artifacts (AppVeyor) <https://www.appveyor.com/docs/packaging-artifacts/ >`_
351
-
352
- Running the tests for pytest-mpl
353
- --------------------------------
354
-
355
- If you are contributing some changes and want to run the tests, first
356
- install the latest version of the plugin then do::
357
-
358
- cd tests
359
- pytest --mpl
360
-
361
- The reason for having to install the plugin first is to ensure that the
362
- plugin is correctly loaded as part of the test suite.
363
-
364
- .. |html all | image :: images/html_all.png
365
- .. |html filter | image :: images/html_filter.png
366
- .. |html result | image :: images/html_result.png
367
- .. |expected | image :: images/baseline-coords_overlay_auto_coord_meta.png
368
- .. |actual | image :: images/coords_overlay_auto_coord_meta.png
369
- .. |diff | image :: images/coords_overlay_auto_coord_meta-failed-diff.png
0 commit comments