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