<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Sean Gillies (Posts about pytest)</title><link>https://sgillies.net/</link><description></description><atom:link href="https://sgillies.net/tags/pytest.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Wed, 06 Nov 2024 00:19:37 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Use pytest's tmp_path fixture</title><link>https://sgillies.net/2024/11/05/use-pytest-tmp-path-fixture.html</link><dc:creator>Sean Gillies</dc:creator><description>&lt;p&gt;If you're not already using pytest's &lt;code class="docutils literal"&gt;tmp_path&lt;/code&gt; &lt;a class="reference external" href="https://docs.pytest.org/en/stable/how-to/tmp_path.html"&gt;fixture&lt;/a&gt;, you really should.
The fixture provides a temporary directory for testing use. A directory that
you can't reference from other test runs or from other test functions in the
same run, but that isn't immediately deleted when your tests finish. The
directory is created in your account temporary location and is eventually
cleaned up by your computer's operating system. Until then, you can open the
directories and their files in other applications.&lt;/p&gt;
&lt;p&gt;I've been making assertions on datasets in Rasterio's tests and also dragging
them into QGIS for a closer look after the tests finish.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="https://live.staticflickr.com/65535/54120218694_cd56609f30_b.jpg" src="https://live.staticflickr.com/65535/54120218694_cd56609f30_b.jpg"&gt;
&lt;/figure&gt;</description><category>programming</category><category>pytest</category><category>python</category><category>qgis</category><category>work</category><guid>https://sgillies.net/2024/11/05/use-pytest-tmp-path-fixture.html</guid><pubDate>Tue, 05 Nov 2024 23:56:14 GMT</pubDate></item><item><title>pytest.raises excinfo subtlety</title><link>https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html</link><dc:creator>Sean Gillies</dc:creator><description>&lt;p&gt;In the past few weeks I've seen multiple stumbles related to a subtlety of
pytest.  I'm going to explain how to recognize the issue and what to do about
it.&lt;/p&gt;
&lt;p&gt;Exceptions are an aspect of a Python package's API, just like the names of the
functions, their parameters, and their return types. They also require testing.
Pytest provides a handy context manager for this: &lt;a class="reference external" href="https://docs.pytest.org/en/latest/reference.html#pytest.raises"&gt;pytest.raises&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-1" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-1" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accept_numbers_lt_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-2" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-2" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-3" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-3" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-3"&gt;&lt;/a&gt;        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Number is not less than 3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-4" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-4" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-5" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-5" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-6" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-6" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_unaccepted_error&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-7" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-7" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-8" name="rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-8" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_4f7f8a4d4c5a485e976c6bd7528a41f0-8"&gt;&lt;/a&gt;        &lt;span class="n"&gt;accept_numbers_lt_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That test will pass. The function does raise a ValueError when called with the
argument 3.&lt;/p&gt;
&lt;p&gt;To make assertions about details of the exception you might try the following.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_2730e636745f41068a8321064f6ed451-1" name="rest_code_2730e636745f41068a8321064f6ed451-1" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_2730e636745f41068a8321064f6ed451-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_unaccepted_error_msg&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_2730e636745f41068a8321064f6ed451-2" name="rest_code_2730e636745f41068a8321064f6ed451-2" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_2730e636745f41068a8321064f6ed451-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_2730e636745f41068a8321064f6ed451-3" name="rest_code_2730e636745f41068a8321064f6ed451-3" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_2730e636745f41068a8321064f6ed451-3"&gt;&lt;/a&gt;        &lt;span class="n"&gt;accept_numbers_lt_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_2730e636745f41068a8321064f6ed451-4" name="rest_code_2730e636745f41068a8321064f6ed451-4" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_2730e636745f41068a8321064f6ed451-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Number is not less than three"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This test passes too. But wait, we mistyped the expected string. We're
asserting that the message ends with "three" and that can't be true, can it?
How did this test pass?&lt;/p&gt;
&lt;p&gt;Here's the important thing: pytest "magically" changes the interpretation of
assert statements, but it doesn't change the behavior of Python's "with"
statements.  That final assert statement in test_unaccepted_error_msg is never
reached. Execution exits from the block when the ValueError (or any other
exception) is raised.&lt;/p&gt;
&lt;p&gt;The excinfo object has recorded the captured exception so that we can
inspect it. We only need to move that assert statement to after the with block.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_367d42a5be2c4c278060eebd0ea51dfc-1" name="rest_code_367d42a5be2c4c278060eebd0ea51dfc-1" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_367d42a5be2c4c278060eebd0ea51dfc-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_unaccepted_error_msg&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_367d42a5be2c4c278060eebd0ea51dfc-2" name="rest_code_367d42a5be2c4c278060eebd0ea51dfc-2" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_367d42a5be2c4c278060eebd0ea51dfc-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_367d42a5be2c4c278060eebd0ea51dfc-3" name="rest_code_367d42a5be2c4c278060eebd0ea51dfc-3" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_367d42a5be2c4c278060eebd0ea51dfc-3"&gt;&lt;/a&gt;        &lt;span class="n"&gt;accept_numbers_lt_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_367d42a5be2c4c278060eebd0ea51dfc-4" name="rest_code_367d42a5be2c4c278060eebd0ea51dfc-4" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_367d42a5be2c4c278060eebd0ea51dfc-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_367d42a5be2c4c278060eebd0ea51dfc-5" name="rest_code_367d42a5be2c4c278060eebd0ea51dfc-5" href="https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html#rest_code_367d42a5be2c4c278060eebd0ea51dfc-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Number is not less than three"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now this test will fail, properly. And we can change "three" to "3" and have
a passing test of an exception message.&lt;/p&gt;
&lt;p&gt;This issue is documented in a note in the pytest.raises docs, but is easy to
overlook.&lt;/p&gt;</description><category>exceptions</category><category>pytest</category><category>python</category><category>testing</category><category>work</category><guid>https://sgillies.net/2020/03/30/pytest-raises-excinfo-subtlety.html</guid><pubDate>Tue, 31 Mar 2020 01:45:36 GMT</pubDate></item><item><title>Debugging temporary files using pytest autouse fixtures</title><link>https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html</link><dc:creator>Sean Gillies</dc:creator><description>&lt;p&gt;This week I discovered that Rasterio doesn't always close the temporary
in-memory datasets that are used within some of its methods. In testing
Rasterio's WarpedVRT class I used a GDAL function to dump descriptions of all
open datasets and found a bunch that looked unrelated to WarpedVRT. They were
GDAL "MEM" type datasets with UUIDs for names, which didn't tell me much. What
were their origins?&lt;/p&gt;
&lt;p&gt;They have UUIDs for names because Rasterio imports uuid in its _io module and
calls &lt;code class="docutils literal"&gt;uuid.uuid4()&lt;/code&gt; to make temporary dataset names. If only the dataset
name included the name of the test in which it was created, then I'd have an
entry point into debugging. One way to do this is with a &lt;a class="reference external" href="https://docs.pytest.org/en/latest/fixture.html#autouse-fixtures-xunit-setup-on-steroids"&gt;pytest auto-used
fixture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I changed the rasterio._io module's import statement from &lt;code class="docutils literal"&gt;import uuid&lt;/code&gt; to
&lt;code class="docutils literal"&gt;from uuid import uuid4&lt;/code&gt; to make it slightly easier to monkey patch and then
I added 5 lines of code to Rasterio's &lt;code class="docutils literal"&gt;conftest.py&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-1" name="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-1" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_3dbb9bcf72ca45ec80304496ab4709ef-1"&gt;&lt;/a&gt;&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autouse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-2" name="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-2" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_3dbb9bcf72ca45ec80304496ab4709ef-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_mem_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-3" name="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-3" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_3dbb9bcf72ca45ec80304496ab4709ef-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;youyoueyedeefour&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-4" name="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-4" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_3dbb9bcf72ca45ec80304496ab4709ef-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a id="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-5" name="rest_code_3dbb9bcf72ca45ec80304496ab4709ef-5" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_3dbb9bcf72ca45ec80304496ab4709ef-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rasterio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"uuid4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;youyoueyedeefour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;code class="docutils literal"&gt;set_mem_name&lt;/code&gt; fixture uses two standard pytest fixtures: &lt;code class="docutils literal"&gt;request&lt;/code&gt;
and &lt;code class="docutils literal"&gt;monkeypatch&lt;/code&gt;. The value of &lt;code class="docutils literal"&gt;request.node.name&lt;/code&gt; is the name of the test
and this &lt;code class="docutils literal"&gt;set_mem_name&lt;/code&gt; fixture uses &lt;code class="docutils literal"&gt;monkeypatch&lt;/code&gt; to replace &lt;code class="docutils literal"&gt;uuid4&lt;/code&gt; in
&lt;code class="docutils literal"&gt;rasterio._io&lt;/code&gt; with a custom function that prepends the name of the test to
the UUID. The &lt;code class="docutils literal"&gt;autouse=True&lt;/code&gt; argument tells pytest to add this fixture to every
test it finds. I didn't need to touch the code of any of Rasterio's tests, not a one.&lt;/p&gt;
&lt;p&gt;This quickly revealed to me that the unclosed temporary datasets were coming
from tests that asserted certain exceptions were being raised by Rasterio's
reprojection code. This code used temporary datasets and didn't close them
before raising the exception to the caller. Once I changed the code to do the
following, Rasterio no longer leaked datasets from those tests, or in our
programs.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-1" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-1" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-2" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-2" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-2"&gt;&lt;/a&gt;    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-3" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-3" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-4" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-4" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;CRSError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-5" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-5" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-5"&gt;&lt;/a&gt;    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-6" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-6" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-7" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-7" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-7"&gt;&lt;/a&gt;    &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-8" name="rest_code_51645f5d81434e3ab3c0ef0af18f5e89-8" href="https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html#rest_code_51645f5d81434e3ab3c0ef0af18f5e89-8"&gt;&lt;/a&gt;    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If Rasterio used only Python's unittest module, and not pytest, it would be
possible to do the same thing. Import rasterio._io in the test case's
&lt;code class="docutils literal"&gt;setUp()&lt;/code&gt;, monkey patch it, and then restore it in &lt;code class="docutils literal"&gt;tearDown()&lt;/code&gt;. If all the
tests derived from one base class, it would only be necessary to extend that
class. The unittest.mock module easily &lt;a class="reference external" href="https://docs.python.org/3/library/unittest.mock-examples.html#applying-the-same-patch-to-every-test-method"&gt;allows every test to be patched with
a single decorator statement&lt;/a&gt;.
It seems like it could be two fewer lines of code, but I don't immediately see
how to get the name of the test and use it with only the mock.patch decorator.
It looks like one would have to use a patcher's &lt;a class="reference external" href="https://docs.python.org/3/library/unittest.mock.html#patch-methods-start-and-stop"&gt;start and stop&lt;/a&gt;,
which is back to somewhat more boilerplate than with pytest.&lt;/p&gt;</description><category>pytest</category><category>python</category><category>testing</category><category>work</category><guid>https://sgillies.net/2019/09/13/debugging-temp-files-using-autouse-fixtures.html</guid><pubDate>Fri, 13 Sep 2019 23:10:48 GMT</pubDate></item></channel></rss>