• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Usage
2=====
3
4Test Scenarios
5--------------
6There are several approaches for implementing tests using ``pyfakefs``.
7
8Patch using the pytest plugin
9~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10``pyfakefs`` functions as a `pytest`_ plugin that provides the `fs` fixture,
11which is registered at installation time.
12Using this fixture automatically patches all file system functions with
13the fake file system functions. It also allows to access several
14convenience methods (see :ref:`convenience_methods`).
15
16Here is an example for a simple test:
17
18.. code:: python
19
20   import os
21
22
23   def test_fakefs(fs):
24       # "fs" is the reference to the fake file system
25       fs.create_file("/var/data/xx1.txt")
26       assert os.path.exists("/var/data/xx1.txt")
27
28If you are bothered by the ``pylint`` warning,
29``C0103: Argument name "fs" doesn't conform to snake_case naming style (invalid-name)``,
30you can define a longer name in your ``conftest.py`` and use that in your tests:
31
32.. code:: python
33
34    import pytest
35
36
37    @pytest.fixture
38    def fake_filesystem(fs):  # pylint:disable=invalid-name
39        """Variable name 'fs' causes a pylint warning. Provide a longer name
40        acceptable to pylint for use in tests.
41        """
42        yield fs
43
44Class-, module- and session-scoped fixtures
45...........................................
46For convenience, class-, module- and session-scoped fixtures with the same
47functionality are provided, named ``fs_class``, ``fs_module`` and ``fs_session``,
48respectively.
49
50.. caution:: If any of these fixtures is active, any other ``fs`` fixture will
51  not setup / tear down the fake filesystem in the current scope; instead, it
52  will just serve as a reference to the active fake filesystem. That means that changes
53  done in the fake filesystem inside a test will remain there until the respective scope
54  ends (see also :ref:`nested_patcher_invocation`).
55
56Patch using fake_filesystem_unittest
57~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58If you are using the Python ``unittest`` package, the easiest approach is to
59use test classes derived from ``fake_filesystem_unittest.TestCase``.
60
61If you call ``setUpPyfakefs()`` in your ``setUp()``, ``pyfakefs`` will
62automatically find all real file functions and modules, and stub these out
63with the fake file system functions and modules:
64
65.. code:: python
66
67    import os
68    from pyfakefs.fake_filesystem_unittest import TestCase
69
70
71    class ExampleTestCase(TestCase):
72        def setUp(self):
73            self.setUpPyfakefs()
74
75        def test_create_file(self):
76            file_path = "/test/file.txt"
77            self.assertFalse(os.path.exists(file_path))
78            self.fs.create_file(file_path)
79            self.assertTrue(os.path.exists(file_path))
80
81The usage is explained in more detail in :ref:`auto_patch` and
82demonstrated in the files `example.py`_ and `example_test.py`_.
83
84If your setup is the same for all tests in a class, you can use the class setup
85method ``setUpClassPyfakefs`` instead:
86
87.. code:: python
88
89    import os
90    import pathlib
91    from pyfakefs.fake_filesystem_unittest import TestCase
92
93
94    class ExampleTestCase(TestCase):
95        @classmethod
96        def setUpClass(cls):
97            cls.setUpClassPyfakefs()
98            # setup the fake filesystem using standard functions
99            path = pathlib.Path("/test")
100            path.mkdir()
101            (path / "file1.txt").touch()
102            # you can also access the fake fs via fake_fs() if needed
103            cls.fake_fs().create_file("/test/file2.txt", contents="test")
104
105        def test1(self):
106            self.assertTrue(os.path.exists("/test/file1.txt"))
107            self.assertTrue(os.path.exists("/test/file2.txt"))
108
109        def test2(self):
110            self.assertTrue(os.path.exists("/test/file1.txt"))
111            file_path = "/test/file3.txt"
112            # self.fs is the same instance as cls.fake_fs() above
113            self.fs.create_file(file_path)
114            self.assertTrue(os.path.exists(file_path))
115
116.. note:: This feature cannot be used with a Python version before Python 3.8 due to
117  a missing feature in ``unittest``. If you use ``pytest`` for running tests using this feature,
118  you need to have at least ``pytest`` version 6.2 due to an issue in earlier versions.
119
120.. caution:: If this is used, any changes made in the fake filesystem inside a test
121  will remain there for all following tests in the test class, if they are not reverted
122  in the test itself.
123
124
125Patch using fake_filesystem_unittest.Patcher
126~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
127If you are using other means of testing like `nose`_,
128you can do the patching using ``fake_filesystem_unittest.Patcher``--the class
129doing the actual work of replacing the filesystem modules with the fake modules
130in the first two approaches.
131
132The easiest way is to just use ``Patcher`` as a context manager:
133
134.. code:: python
135
136   from pyfakefs.fake_filesystem_unittest import Patcher
137
138   with Patcher() as patcher:
139       # access the fake_filesystem object via patcher.fs
140       patcher.fs.create_file("/foo/bar", contents="test")
141
142       # the following code works on the fake filesystem
143       with open("/foo/bar") as f:
144           contents = f.read()
145
146You can also initialize ``Patcher`` manually:
147
148.. code:: python
149
150   from pyfakefs.fake_filesystem_unittest import Patcher
151
152   patcher = Patcher()
153   patcher.setUp()  # called in the initialization code
154   ...
155   patcher.tearDown()  # somewhere in the cleanup code
156
157Patch using fake_filesystem_unittest.patchfs decorator
158~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159This is basically a convenience wrapper for the previous method.
160If you are not using ``pytest`` and  want to use the fake filesystem for a
161single function, you can write:
162
163.. code:: python
164
165   from pyfakefs.fake_filesystem_unittest import patchfs
166
167
168   @patchfs
169   def test_something(fake_fs):
170       # access the fake_filesystem object via fake_fs
171       fake_fs.create_file("/foo/bar", contents="test")
172
173Note that ``fake_fs`` is a positional argument and the argument name does
174not matter. If there are additional ``mock.patch`` decorators that also
175create positional arguments, the argument order is the same as the decorator
176order, as shown here:
177
178.. code:: python
179
180   @patchfs
181   @mock.patch("foo.bar")
182   def test_something(fake_fs, mocked_bar):
183       assert foo()
184
185
186   @mock.patch("foo.bar")
187   @patchfs
188   def test_something(mocked_bar, fake_fs):
189       assert foo()
190
191.. note::
192  Avoid writing the ``patchfs`` decorator *between* ``mock.patch`` operators,
193  as the order will not be what you expect. Due to implementation details,
194  all arguments created by ``mock.patch`` decorators are always expected to
195  be contiguous, regardless of other decorators positioned between them.
196
197.. caution::
198  In previous versions, the keyword argument `fs` has been used instead,
199  which had to be positioned *after* all positional arguments regardless of
200  the decorator order. If you upgrade from a version before pyfakefs 4.2,
201  you may have to adapt the argument order.
202
203You can also use this to make a single unit test use the fake fs:
204
205.. code:: python
206
207    class TestSomething(unittest.TestCase):
208        @patchfs
209        def test_something(self, fs):
210            fs.create_file("/foo/bar", contents="test")
211
212
213.. _customizing_patcher:
214
215Customizing patching
216--------------------
217
218``fake_filesystem_unittest.Patcher`` provides a few arguments to adapt
219patching for cases where it does not work out of the box. These arguments
220can also be used with ``unittest`` and ``pytest``.
221
222Using custom arguments
223~~~~~~~~~~~~~~~~~~~~~~
224The following sections describe how to apply these arguments in different
225scenarios, using the argument :ref:`allow_root_user` as an example.
226
227Patcher
228.......
229If you use the ``Patcher`` directly, you can just pass the arguments in the
230constructor:
231
232.. code:: python
233
234  from pyfakefs.fake_filesystem_unittest import Patcher
235
236  with Patcher(allow_root_user=False) as patcher:
237      ...
238
239Pytest
240......
241
242In case of ``pytest``, you have two possibilities:
243
244- The standard way to customize the ``fs`` fixture is to write your own
245  fixture which uses the ``Patcher`` with arguments as has been shown above:
246
247.. code:: python
248
249  import pytest
250  from pyfakefs.fake_filesystem_unittest import Patcher
251
252
253  @pytest.fixture
254  def fs_no_root():
255      with Patcher(allow_root_user=False) as patcher:
256          yield patcher.fs
257
258
259  def test_something(fs_no_root):
260      assert foo()
261
262- You can also pass the arguments using ``@pytest.mark.parametrize``. Note that
263  you have to provide `all Patcher arguments`_ before the needed ones, as
264  keyword arguments cannot be used, and you have to add ``indirect=True``.
265  This makes it less readable, but gives you a quick possibility to adapt a
266  single test:
267
268.. code:: python
269
270  import pytest
271
272
273  @pytest.mark.parametrize("fs", [[None, None, None, False]], indirect=True)
274  def test_something(fs):
275      assert foo()
276
277Unittest
278........
279If you are using ``fake_filesystem_unittest.TestCase``, the arguments can be
280passed to ``setUpPyfakefs()``, which will pass them to the ``Patcher``
281instance:
282
283.. code:: python
284
285  from pyfakefs.fake_filesystem_unittest import TestCase
286
287
288  class SomeTest(TestCase):
289      def setUp(self):
290          self.setUpPyfakefs(allow_root_user=False)
291
292      def testSomething(self):
293          assert foo()
294
295patchfs
296.......
297If you use the ``patchfs`` decorator, you can pass the arguments directly to
298the decorator:
299
300.. code:: python
301
302  from pyfakefs.fake_filesystem_unittest import patchfs
303
304
305  @patchfs(allow_root_user=False)
306  def test_something(fake_fs):
307      assert foo()
308
309
310List of custom arguments
311~~~~~~~~~~~~~~~~~~~~~~~~
312
313Following is a description of the optional arguments that can be used to
314customize ``pyfakefs``.
315
316.. _modules_to_reload:
317
318modules_to_reload
319.................
320``Pyfakefs`` patches modules that are imported before starting the test by
321finding and replacing file system modules in all loaded modules at test
322initialization time.
323This allows to automatically patch file system related modules that are:
324
325- imported directly, for example:
326
327.. code:: python
328
329  import os
330  import pathlib.Path
331
332- imported as another name:
333
334.. code:: python
335
336  import os as my_os
337
338- imported using one of these two specially handled statements:
339
340.. code:: python
341
342  from os import path
343  from pathlib import Path
344
345Additionally, functions from file system related modules are patched
346automatically if imported like:
347
348.. code:: python
349
350  from os.path import exists
351  from os import stat
352
353This also works if importing the functions as another name:
354
355.. code:: python
356
357  from os.path import exists as my_exists
358  from io import open as io_open
359  from builtins import open as bltn_open
360
361There are a few cases where automatic patching does not work. We know of at
362least two specific cases where this is the case:
363
364Initializing a default argument with a file system function is not patched
365automatically due to performance reasons (though it can be switched on using
366:ref:`patch_default_args`):
367
368.. code:: python
369
370  import os
371
372
373  def check_if_exists(filepath, file_exists=os.path.exists):
374      return file_exists(filepath)
375
376
377If initializing a global variable using a file system function, the
378initialization will be done using the real file system:
379
380.. code:: python
381
382  from pathlib import Path
383
384  path = Path("/example_home")
385
386In this case, ``path`` will hold the real file system path inside the test.
387The same is true, if a file system function is used in a decorator (this is
388an example from a related issue):
389
390.. code:: python
391
392  import pathlib
393  import click
394
395
396  @click.command()
397  @click.argument("foo", type=click.Path(path_type=pathlib.Path))
398  def hello(foo):
399      pass
400
401To get these cases to work as expected under test, the respective modules
402containing the code shall be added to the ``modules_to_reload`` argument (a
403module list).
404The passed modules will be reloaded, thus allowing ``pyfakefs`` to patch them
405dynamically. All modules loaded after the initial patching described above
406will be patched using this second mechanism.
407
408Given that the example function ``check_if_exists`` shown above is located in
409the file ``example/sut.py``, the following code will work (imports are omitted):
410
411.. code:: python
412
413  import example
414
415
416  # example using unittest
417  class ReloadModuleTest(fake_filesystem_unittest.TestCase):
418      def setUp(self):
419          self.setUpPyfakefs(modules_to_reload=[example.sut])
420
421      def test_path_exists(self):
422          file_path = "/foo/bar"
423          self.fs.create_dir(file_path)
424          self.assertTrue(example.sut.check_if_exists(file_path))
425
426
427  # example using pytest
428  @pytest.mark.parametrize("fs", [[None, [example.sut]]], indirect=True)
429  def test_path_exists(fs):
430      file_path = "/foo/bar"
431      fs.create_dir(file_path)
432      assert example.sut.check_if_exists(file_path)
433
434
435  # example using Patcher
436  def test_path_exists():
437      with Patcher(modules_to_reload=[example.sut]) as patcher:
438          file_path = "/foo/bar"
439          patcher.fs.create_dir(file_path)
440          assert example.sut.check_if_exists(file_path)
441
442
443  # example using patchfs decorator
444  @patchfs(modules_to_reload=[example.sut])
445  def test_path_exists(fs):
446      file_path = "/foo/bar"
447      fs.create_dir(file_path)
448      assert example.sut.check_if_exists(file_path)
449
450
451.. note:: If the reloaded modules depend on each other (e.g. one imports the other),
452  the order in which they are reloaded matters. The dependent module should be reloaded
453  first, so that on reloading the depending module it is already correctly patched.
454
455
456modules_to_patch
457................
458Sometimes there are file system modules in other packages that are not
459patched in standard ``pyfakefs``. To allow patching such modules,
460``modules_to_patch`` can be used by adding a fake module implementation for
461a module name. The argument is a dictionary of fake modules mapped to the
462names to be faked.
463
464This mechanism is used in ``pyfakefs`` itself to patch the external modules
465`pathlib2` and `scandir` if present, and the following example shows how to
466fake a module in Django that uses OS file system functions (note that this
467has now been been integrated into ``pyfakefs``):
468
469.. code:: python
470
471  import django
472
473
474  class FakeLocks:
475      """django.core.files.locks uses low level OS functions, fake it."""
476
477      _locks_module = django.core.files.locks
478
479      def __init__(self, fs):
480          """Each fake module expects the fake file system as an __init__
481          parameter."""
482          # fs represents the fake filesystem; for a real example, it can be
483          # saved here and used in the implementation
484          pass
485
486      @staticmethod
487      def lock(f, flags):
488          return True
489
490      @staticmethod
491      def unlock(f):
492          return True
493
494      def __getattr__(self, name):
495          return getattr(self._locks_module, name)
496
497
498  ...
499  # test code using Patcher
500  with Patcher(modules_to_patch={"django.core.files.locks": FakeLocks}):
501      test_django_stuff()
502
503
504  # test code using unittest
505  class TestUsingDjango(fake_filesystem_unittest.TestCase):
506      def setUp(self):
507          self.setUpPyfakefs(modules_to_patch={"django.core.files.locks": FakeLocks})
508
509      def test_django_stuff(self):
510          assert foo()
511
512
513  # test code using pytest
514  @pytest.mark.parametrize(
515      "fs", [[None, None, {"django.core.files.locks": FakeLocks}]], indirect=True
516  )
517  def test_django_stuff(fs):
518      assert foo()
519
520
521  # test code using patchfs decorator
522  @patchfs(modules_to_patch={"django.core.files.locks": FakeLocks})
523  def test_django_stuff(fake_fs):
524      assert foo()
525
526additional_skip_names
527.....................
528This may be used to add modules that shall not be patched. This is mostly
529used to avoid patching the Python file system modules themselves, but may be
530helpful in some special situations, for example if a testrunner needs to access
531the file system after test setup. To make this possible, the affected module
532can be added to ``additional_skip_names``:
533
534.. code:: python
535
536  with Patcher(additional_skip_names=["pydevd"]) as patcher:
537      patcher.fs.create_file("foo")
538
539Alternatively to the module names, the modules themselves may be used:
540
541.. code:: python
542
543  import pydevd
544  from pyfakefs.fake_filesystem_unittest import Patcher
545
546  with Patcher(additional_skip_names=[pydevd]) as patcher:
547      patcher.fs.create_file("foo")
548
549.. _allow_root_user:
550
551allow_root_user
552...............
553This is ``True`` by default, meaning that the user is considered a root user
554if the real user is a root user (e.g. has the user ID 0). If you want to run
555your tests as a non-root user regardless of the actual user rights, you may
556want to set this to ``False``.
557
558use_known_patches
559.................
560Some libraries are known to require patching in order to work with
561``pyfakefs``.
562If ``use_known_patches`` is set to ``True`` (the default), ``pyfakefs`` patches
563these libraries so that they will work with the fake filesystem. Currently, this
564includes patches for ``pandas`` read methods like ``read_csv`` and
565``read_excel``, and for ``Django`` file locks--more may follow. Ordinarily,
566the default value of ``use_known_patches`` should be used, but it is present
567to allow users to disable this patching in case it causes any problems.
568
569patch_open_code
570...............
571Since Python 3.8, the ``io`` module has the function ``open_code``, which
572opens a file read-only and is used to open Python code files. By default, this
573function is not patched, because the files it opens usually belong to the
574executed library code and are not present in the fake file system.
575Under some circumstances, this may not be the case, and the opened file
576lives in the fake filesystem. For these cases, you can set ``patch_open_code``
577to ``PatchMode.ON``. If you just want to patch ``open_case`` for files that
578live in the fake filesystem, and use the real function for the rest, you can
579set ``patch_open_code`` to ``PatchMode.AUTO``:
580
581.. code:: python
582
583  from pyfakefs.fake_filesystem_unittest import PatchMode
584
585
586  @patchfs(patch_open_code=PatchMode.AUTO)
587  def test_something(fs):
588      assert foo()
589
590In this mode, it is also possible to import modules created in the fake filesystem
591using `importlib.import_module`. Make sure that the `sys.path` contains the parent path in this case:
592
593.. code:: python
594
595  @patchfs(patch_open_code=PatchMode.AUTO)
596  def test_fake_import(fs):
597      fake_module_path = Path("/") / "site-packages" / "fake_module.py"
598      self.fs.create_file(fake_module_path, contents="x = 5")
599      sys.path.insert(0, str(fake_module_path.parent))
600      module = importlib.import_module("fake_module")
601      assert module.x == 5
602
603
604.. _patch_default_args:
605
606patch_default_args
607..................
608As already mentioned, a default argument that is initialized with a file
609system function is not patched automatically:
610
611.. code:: python
612
613  import os
614
615
616  def check_if_exists(filepath, file_exists=os.path.exists):
617      return file_exists(filepath)
618
619As this is rarely needed, and the check to patch this automatically is quite
620expansive, it is not done by default. Using ``patch_default_args`` will
621search for this kind of default arguments and patch them automatically.
622You could also use the :ref:`modules_to_reload` option with the module that
623contains the default argument instead, if you want to avoid the overhead.
624
625.. note:: There are some cases where this option does *not* work:
626
627  - if default arguments are *computed* using file system functions:
628
629    .. code:: python
630
631      import os
632
633
634      def some_function(use_bar=os.path.exists("/foo/bar")):
635          return do_something() if use_bar else do_something_else()
636
637  - if the default argument is an instance of ``pathlib.Path``:
638
639    .. code:: python
640
641      import pathlib
642
643
644      def foobar(dir_arg=pathlib.Path.cwd() / "logs"):
645          do_something(dir_arg)
646
647  In both cases the default arguments behave like global variables that use a file system function
648  (which they basically are), and can only be handled using :ref:`modules_to_reload`.
649
650
651use_cache
652.........
653If True (the default), patched and non-patched modules are cached between tests
654to avoid the performance hit of the file system function lookup (the
655patching itself is reverted after each test). This argument allows to turn it off in case it causes any problems:
656
657.. code:: python
658
659  @patchfs(use_cache=False)
660  def test_something(fake_fs):
661      fake_fs.create_file("foo", contents="test")
662      ...
663
664If using ``pytest``, the cache is always cleared before the final test shutdown, as there has been a problem
665happening on shutdown related to removing the cached modules.
666This does not happen for other test methods so far.
667
668If you think you have encountered a similar problem with ``unittest``, you may try to clear the cache
669during module shutdown using the class method for clearing the cache:
670
671.. code:: python
672
673  from pyfakefs.fake_filesystem_unittest import Patcher
674
675
676  def tearDownModule():
677      Patcher.clear_fs_cache()
678
679Please write an issue if you encounter any problem that can be fixed by using this parameter.
680
681If you want to clear the cache just for a specific test instead, you can call
682``clear_cache`` on the ``Patcher`` or the ``fake_filesystem`` instance:
683
684.. code:: python
685
686  def test_something(fs):  # using pytest fixture
687      fs.clear_cache()
688      ...
689
690.. _use_dynamic_patch:
691
692use_dynamic_patch
693~~~~~~~~~~~~~~~~~
694If ``True`` (the default), dynamic patching after setup is used (for example
695for modules loaded locally inside of functions).
696Can be switched off if it causes unwanted side effects, though that would mean that
697dynamically loaded modules are no longer patched, if they use file system functions.
698See also :ref:`failing_dyn_patcher` in the troubleshooting guide for more information.
699
700
701.. _convenience_methods:
702
703Using convenience methods
704-------------------------
705While ``pyfakefs`` can be used just with the standard Python file system
706functions, there are a few convenience methods in ``fake_filesystem`` that can
707help you setting up your tests. The methods can be accessed via the
708``fake_filesystem`` instance in your tests: ``Patcher.fs``, the ``fs``
709fixture in pytest, ``TestCase.fs`` for ``unittest``, and the positional argument
710for the ``patchfs`` decorator.
711
712File creation helpers
713~~~~~~~~~~~~~~~~~~~~~
714To create files, directories or symlinks together with all the directories
715in the path, you may use :py:meth:`create_file()<pyfakefs.fake_filesystem.FakeFilesystem.create_file>`,
716:py:meth:`create_dir()<pyfakefs.fake_filesystem.FakeFilesystem.create_dir>`,
717:py:meth:`create_symlink()<pyfakefs.fake_filesystem.FakeFilesystem.create_symlink>` and
718:py:meth:`create_link()<pyfakefs.fake_filesystem.FakeFilesystem.create_link>`, respectively.
719
720``create_file()`` also allows you to set the file mode and the file contents
721together with the encoding if needed. Alternatively, you can define a file
722size without contents--in this case, you will not be able to perform
723standard I\O operations on the file (may be used to fill up the file system
724with large files, see also :ref:`set-fs-size`).
725
726.. code:: python
727
728    from pyfakefs.fake_filesystem_unittest import TestCase
729
730
731    class ExampleTestCase(TestCase):
732        def setUp(self):
733            self.setUpPyfakefs()
734
735        def test_create_file(self):
736            file_path = "/foo/bar/test.txt"
737            self.fs.create_file(file_path, contents="test")
738            with open(file_path) as f:
739                self.assertEqual("test", f.read())
740
741``create_dir()`` behaves like ``os.makedirs()``.
742``create_symlink`` and ``create_link`` behave like ``os.symlink`` and
743``os.link``, with any missing parent directories of the link created
744automatically.
745
746.. caution::
747  The first two arguments in ``create_symlink`` are reverted in relation to
748  ``os.symlink`` for historical reasons.
749
750.. _real_fs_access:
751
752Access to files in the real file system
753~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
754If you want to have read access to real files or directories, you can map
755them into the fake file system using :py:meth:`add_real_file()<pyfakefs.fake_filesystem.FakeFilesystem.add_real_file>`,
756:py:meth:`add_real_directory()<pyfakefs.fake_filesystem.FakeFilesystem.add_real_directory>`,
757:py:meth:`add_real_symlink()<pyfakefs.fake_filesystem.FakeFilesystem.add_real_symlink>` and
758:py:meth:`add_real_paths()<pyfakefs.fake_filesystem.FakeFilesystem.add_real_paths>`.
759They take a file path, a directory path, a symlink path, or a list of paths,
760respectively, and make them accessible from the fake file system. By
761default, the contents of the mapped files and directories are read only on
762demand, so that mapping them is relatively cheap. The access to the files is
763by default read-only, but even if you add them using ``read_only=False``,
764the files are written only in the fake system (e.g. in memory). The real
765files are never changed.
766
767``add_real_file()``, ``add_real_directory()`` and ``add_real_symlink()`` also
768allow you to map a file or a directory tree into another location in the
769fake filesystem via the argument ``target_path``. If the target directory already exists
770in the fake filesystem, the directory contents are merged. If a file in the fake filesystem
771would be overwritten by a file from the real filesystem, an exception is raised.
772
773.. code:: python
774
775    import os
776    from pyfakefs.fake_filesystem_unittest import TestCase
777
778
779    class ExampleTestCase(TestCase):
780
781        fixture_path = os.path.join(os.path.dirname(__file__), "fixtures")
782
783        def setUp(self):
784            self.setUpPyfakefs()
785            # make the file accessible in the fake file system
786            self.fs.add_real_directory(self.fixture_path)
787
788        def test_using_fixture(self):
789            with open(os.path.join(self.fixture_path, "fixture1.txt")) as f:
790                # file contents are copied to the fake file system
791                # only at this point
792                contents = f.read()
793
794You can do the same using ``pytest`` by using a fixture for test setup:
795
796.. code:: python
797
798    import pytest
799    import os
800
801    fixture_path = os.path.join(os.path.dirname(__file__), "fixtures")
802
803
804    @pytest.fixture
805    def my_fs(fs):
806        fs.add_real_directory(fixture_path)
807        yield fs
808
809
810    @pytest.mark.usefixtures("my_fs")
811    def test_using_fixture():
812        with open(os.path.join(fixture_path, "fixture1.txt")) as f:
813            contents = f.read()
814
815.. note::
816  If you are not using the fixture directly in the test, you can use
817  ``@pytest.mark.usefixtures`` instead of passing the fixture as an argument.
818  This avoids warnings about unused arguments from linters.
819
820When using ``pytest`` another option is to load the contents of the real file
821in a fixture and pass this fixture to the test function **before** passing
822the ``fs`` fixture.
823
824.. code:: python
825
826    import pytest
827    import os
828
829
830    @pytest.fixture
831    def content():
832        fixture_path = os.path.join(os.path.dirname(__file__), "fixtures")
833        with open(os.path.join(fixture_path, "fixture1.txt")) as f:
834            contents = f.read()
835        return contents
836
837
838    def test_using_file_contents(content, fs):
839        fs.create_file("fake/path.txt")
840        assert content != ""
841
842
843Handling mount points
844~~~~~~~~~~~~~~~~~~~~~
845Under Linux and macOS, the root path (``/``) is the only mount point created
846in the fake file system. If you need support for more mount points, you can add
847them using :py:meth:`add_mount_point()<pyfakefs.fake_filesystem.FakeFilesystem.add_mount_point>`.
848
849Under Windows, drives and UNC paths are internally handled as mount points.
850Adding a file or directory on another drive or UNC path automatically
851adds a mount point for that drive or UNC path root if needed. Explicitly
852adding mount points shall not be needed under Windows.
853
854A mount point has a separate device ID (``st_dev``) under all systems, and
855some operations (like ``rename``) are not possible for files located on
856different mount points. The fake file system size (if used) is also set per
857mount point.
858
859.. _set-fs-size:
860
861Setting the file system size
862~~~~~~~~~~~~~~~~~~~~~~~~~~~~
863If you need to know the file system size in your tests (for example for
864testing cleanup scripts), you can set the fake file system size using
865:py:meth:`set_disk_usage()<pyfakefs.fake_filesystem.FakeFilesystem.set_disk_usage>`. By default, this sets the total size in bytes of the
866root partition; if you add a path as parameter, the size will be related to
867the mount point (see above) the path is related to.
868
869By default, the size of the fake file system is set to 1 TB (which
870for most tests can be considered as infinite). As soon as you set a
871size, all files will occupy the space according to their size,
872and you may fail to create new files if the fake file system is full.
873
874.. code:: python
875
876    import errno
877    import os
878    from pyfakefs.fake_filesystem_unittest import TestCase
879
880
881    class ExampleTestCase(TestCase):
882        def setUp(self):
883            self.setUpPyfakefs()
884            self.fs.set_disk_usage(100)
885
886        def test_disk_full(self):
887            os.mkdir("/foo")
888            with self.assertRaises(OSError) as e:
889                with open("/foo/bar.txt", "w") as f:
890                    f.write("a" * 200)
891            self.assertEqual(errno.ENOSPC, e.exception.errno)
892
893To get the file system size, you may use :py:meth:`get_disk_usage()<pyfakefs.fake_filesystem.FakeFilesystem.get_disk_usage>`, which is
894modeled after ``shutil.disk_usage()``.
895
896Suspending patching
897~~~~~~~~~~~~~~~~~~~
898Sometimes, you may want to access the real filesystem inside the test with
899no patching applied. This can be achieved by using the ``pause/resume``
900functions, which exist in ``fake_filesystem_unittest.Patcher``,
901``fake_filesystem_unittest.TestCase`` and ``fake_filesystem.FakeFilesystem``.
902There is also a context manager class ``fake_filesystem_unittest.Pause``
903which encapsulates the calls to ``pause()`` and ``resume()``.
904
905Here is an example that tests the usage with the ``pyfakefs`` pytest fixture:
906
907.. code:: python
908
909    import os
910    import tempfile
911    from pyfakefs.fake_filesystem_unittest import Pause
912
913
914    def test_pause_resume_contextmanager(fs):
915        fake_temp_file = tempfile.NamedTemporaryFile()
916        assert os.path.exists(fake_temp_file.name)
917        fs.pause()
918        assert not os.path.exists(fake_temp_file.name)
919        real_temp_file = tempfile.NamedTemporaryFile()
920        assert os.path.exists(real_temp_file.name)
921        fs.resume()
922        assert not os.path.exists(real_temp_file.name)
923        assert os.path.exists(fake_temp_file.name)
924
925Here is the same code using a context manager:
926
927.. code:: python
928
929    import os
930    import tempfile
931    from pyfakefs.fake_filesystem_unittest import Pause
932
933
934    def test_pause_resume_contextmanager(fs):
935        fake_temp_file = tempfile.NamedTemporaryFile()
936        assert os.path.exists(fake_temp_file.name)
937        with Pause(fs):
938            assert not os.path.exists(fake_temp_file.name)
939            real_temp_file = tempfile.NamedTemporaryFile()
940            assert os.path.exists(real_temp_file.name)
941        assert not os.path.exists(real_temp_file.name)
942        assert os.path.exists(fake_temp_file.name)
943
944Simulating other file systems
945~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
946``Pyfakefs`` supports Linux, macOS and Windows operating systems. By default,
947the file system of the OS where the tests run is assumed, but it is possible
948to simulate other file systems to some extent. To set a specific file
949system, you can change ``pyfakefs.FakeFilesystem.os`` to one of
950``OSType.LINUX``, ``OSType.MACOS`` and ``OSType.WINDOWS``. On doing so, the
951behavior of ``pyfakefs`` is adapted to the respective file system. Note that
952setting this causes the fake file system to be reset, so you should call it
953before adding any files.
954
955Setting the ``os`` attributes changes a number of ``pyfakefs.FakeFilesystem``
956attributes, which can also be set separately if needed:
957
958  - ``is_windows_fs`` -  if ``True`` a Windows file system (NTFS) is assumed
959  - ``is_macos`` - if ``True`` and ``is_windows_fs`` is ``False``, the
960    standard macOS file system (HFS+) is assumed
961  - if ``is_windows_fs`` and ``is_macos`` are ``False``, a Linux file system
962    (something like ext3) is assumed
963  - ``is_case_sensitive`` is set to ``True`` under Linux and to ``False``
964    under Windows and macOS by default - you can change it to change the
965    respective behavior
966  - ``path_separator`` is set to ``\`` under Windows and to ``/`` under Posix,
967    ``alternative_path_separator`` is set to ``/`` under Windows and to
968    ``None`` under Posix--these can also be adapted if needed
969
970The following test works both under Windows and Linux:
971
972.. code:: python
973
974  import os
975  from pyfakefs.fake_filesystem import OSType
976
977
978  def test_windows_paths(fs):
979      fs.os = OSType.WINDOWS
980      assert r"C:\foo\bar" == os.path.join("C:\\", "foo", "bar")
981      assert os.path.splitdrive(r"C:\foo\bar") == ("C:", r"\foo\bar")
982      assert os.path.ismount("C:")
983
984.. note:: Only behavior not relying on OS-specific functionality is emulated on another system.
985  For example, if you use the Linux-specific functionality of extended attributes (``os.getxattr`` etc.)
986  in your code, you have to test this under Linux.
987
988Set file as inaccessible under Windows
989~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
990Normally, if you try to set a file or directory as inaccessible using ``chmod`` under
991Windows, the value you provide is masked by a value that always ensures that no read
992permissions for any user are removed. In reality, there is the possibility to make
993a file or directory unreadable using the Windows ACL API, which is not directly
994supported in the Python filesystem API. To make this possible to test, there is the
995possibility to use the ``force_unix_mode`` argument to ``FakeFilesystem.chmod``:
996
997.. code:: python
998
999    import pathlib
1000    import pytest
1001    from pyfakefs.fake_filesystem import OSType
1002
1003
1004    def test_is_file_for_unreadable_dir_windows(fs):
1005        fs.os = OSType.WINDOWS
1006        path = pathlib.Path("/foo/bar")
1007        fs.create_file(path)
1008        # normal chmod does not really set the mode to 0
1009        fs.chmod("/foo", 0o000)
1010        assert path.is_file()
1011        # but it does in forced UNIX mode
1012        fs.chmod("/foo", 0o000, force_unix_mode=True)
1013        with pytest.raises(PermissionError):
1014            path.is_file()
1015
1016
1017.. _`example.py`: https://github.com/pytest-dev/pyfakefs/blob/main/pyfakefs/tests/example.py
1018.. _`example_test.py`: https://github.com/pytest-dev/pyfakefs/blob/main/pyfakefs/tests/example_test.py
1019.. _`pytest`: https://doc.pytest.org
1020.. _`nose`: https://docs.nose2.io/en/latest/
1021.. _`all Patcher arguments`: https://pytest-pyfakefs.readthedocs.io/en/latest/modules.html#pyfakefs.fake_filesystem_unittest.Patcher
1022