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