Skip to content

Commit c74cd55

Browse files
committed
Rework chapter about fixtures working in the filesystem
1 parent d2d93ad commit c74cd55

File tree

1 file changed

+69
-22
lines changed

1 file changed

+69
-22
lines changed

docs/troubleshooting.rst

+69-22
Original file line numberDiff line numberDiff line change
@@ -239,17 +239,30 @@ is the convenience argument :ref:`allow_root_user`:
239239
If accessing files as another user and/or group, the respective group/other file
240240
permissions are considered.
241241

242+
Interaction with other fixtures working on the filesystem
243+
---------------------------------------------------------
244+
Generally, if you are using a pytest fixture working on the filesystem,
245+
you should check if you really need it. Chances are that the fixture does
246+
something that could be more naturally achieved with ``pyfakefs`` using
247+
standard file system functions.
248+
If you really *do* need such fixtures, the order in which they are used with
249+
respect to the ``fs`` fixture does matter. If the fixture should work with
250+
the fake filesystem instead of the real filesystem, it should be placed
251+
*after* the ``fs`` fixture. If it shall be used with the real filesystem
252+
instead (a case that should almost never be needed), it shall be placed *before*
253+
the ``fs`` fixture.
254+
Following are some related examples.
255+
242256
.. _usage_with_mock_open:
243257

244258
Pyfakefs and mock_open
245-
----------------------
259+
~~~~~~~~~~~~~~~~~~~~~~
246260
If you patch ``open`` using ``mock_open`` before the initialization of
247261
``pyfakefs``, it will not work properly, because the ``pyfakefs``
248262
initialization relies on ``open`` working correctly.
249-
Generally, you should not need ``mock_open`` if using ``pyfakefs``, because you
250-
always can create the files with the needed content using ``create_file``.
251-
This is true for patching any filesystem functions--avoid patching them
252-
while working with ``pyfakefs``.
263+
Generally, you should not need ``mock_open`` if using ``pyfakefs`` at all,
264+
because you can always create the files with the needed content using
265+
``create_file`` or the standard filesystem functions.
253266
If you still want to use ``mock_open``, make sure it is only used while
254267
patching is in progress. For example, if you are using ``pytest`` with the
255268
``mocker`` fixture used to patch ``open``, make sure that the ``fs`` fixture is
@@ -266,29 +279,63 @@ passed before the ``mocker`` fixture to ensure this:
266279
# works correctly
267280
mocker.patch("builtins.open", mocker.mock_open(read_data="content"))
268281
269-
tmp_path fixture with pyfakefs
270-
------------------------------
271-
If you are using the ``tmp_path`` fixture, or a similar pytest fixture
272-
relying on the real filesystem, you may have the opposite problem: now the ``fs``
273-
fixture must be added *after* the ``tmp_path`` fixture. Otherwise, the path will be
274-
created in the fake filesystem, and pytest will later try to access it in the real
275-
filesystem.
282+
The tmp_path fixture
283+
~~~~~~~~~~~~~~~~~~~~
284+
The ``tmp_path`` fixture is an example for a fixture that always works on the real
285+
filesystem. If you invoke it after the *fs* fixture, the path will be
286+
created in the fake filesystem, but pytest will later try to access it in the real
287+
filesystem. While invoking it before the *fs* fixture will technically work, it makes
288+
no real sense. The temporary directory will be created and removed in the real
289+
filesystem, but never actually used by the test that is working in the fake filesystem.
290+
291+
A replacement for the tmp_path fixture
292+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293+
As an example for a useful fixture working on the filesystem, let's write a simple
294+
replacement for ``tmp_path`` that uses ``tempfile.TemporaryDirectory``.
295+
296+
There are two ways to do this with respect to the ``fs`` fixture. The common
297+
way is to base your fixture on the ``fs`` fixture (imports are omitted):
298+
299+
.. code:: python
300+
301+
@pytest.fixture
302+
def temp_path(fs):
303+
tmp_dir = tempfile.TemporaryDirectory()
304+
yield Path(tmp_dir.name)
305+
306+
307+
def test_temp_path(temp_path):
308+
assert temp_path.exists()
309+
assert isinstance(temp_path, fake_pathlib.FakePath)
310+
311+
This way, the fixture will always use the fake filesystem. Note that you can
312+
add the ``fs`` fixture to the test additionally, if you need to access any
313+
convenience function in the fake filesystem.
314+
315+
The other possibility is to write the fixture independently of ``pyfakefs``,
316+
so it can be used both in tests with the real filesystem and with the fake
317+
filesystem:
276318

277319
.. code:: python
278320
279-
def test_working(tmp_path, fs):
280-
fs.create_file(tmp_path / "foo")
321+
@pytest.fixture
322+
def temp_path():
323+
tmp_dir = tempfile.TemporaryDirectory()
324+
yield Path(tmp_dir.name)
325+
326+
327+
def test_real_temp_path(temp_path):
328+
assert temp_path.exists()
329+
# we do not check for pathlib.Path, because FakePath is derived from it
330+
assert isinstance(temp_path, (pathlib.PosixPath, pathlib.WindowsPath))
281331
282332
283-
def test_not_working(fs, tmp_path):
284-
# causes an error while pytest tries to access the temporary directory
285-
pass
333+
def test_fake_temp_path(fs, temp_path):
334+
assert temp_path.exists()
335+
assert isinstance(temp_path, fake_pathlib.FakePath)
286336
287-
Note though that ``tmp_path`` and similar fixtures may not make much sense in the
288-
first place if used with ``pyfakefs``. While the directory will be created and
289-
removed in the real filesystem, all files you create will live in the fake filesystem
290-
only. You could as well use hardcoded paths in your tests, as they will not interfere
291-
with paths created in other tests.
337+
Note that in the last case, your fixture must be invoked *after* the ``fs``
338+
fixture, in order for it to work in the fake filesystem.
292339

293340
Pathlib.Path objects created outside of tests
294341
---------------------------------------------

0 commit comments

Comments
 (0)