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