| Usage |
| ===== |
| |
| Test Scenarios |
| -------------- |
| There are several approaches to implementing tests using ``pyfakefs``. |
| |
| Patch using fake_filesystem_unittest |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| If you are using the Python ``unittest`` package, the easiest approach is to |
| use test classes derived from ``fake_filesystem_unittest.TestCase``. |
| |
| If you call ``setUpPyfakefs()`` in your ``SetUp()``, ``pyfakefs`` will |
| automatically find all real file functions and modules, and stub these out |
| with the fake file system functions and modules: |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import TestCase |
| |
| class ExampleTestCase(TestCase): |
| def setUp(self): |
| self.setUpPyfakefs() |
| |
| def test_create_file(self): |
| file_path = '/test/file.txt' |
| self.assertFalse(os.path.exists(file_path)) |
| self.fs.create_file(file_path) |
| self.assertTrue(os.path.exists(file_path)) |
| |
| The usage is explained in more detail in :ref:`auto_patch` and |
| demonstrated in the files ``example.py`` and ``example_test.py``. |
| |
| Patch using the PyTest plugin |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| If you use `PyTest <https://doc.pytest.org>`__, you will be interested in |
| the PyTest plugin in ``pyfakefs``. |
| This automatically patches all file system functions and modules in a |
| similar manner as described above. |
| |
| The PyTest plugin provides the ``fs`` fixture for use in your test. For example: |
| |
| .. code:: python |
| |
| def my_fakefs_test(fs): |
| # "fs" is the reference to the fake file system |
| fs.create_file('/var/data/xx1.txt') |
| assert os.path.exists('/var/data/xx1.txt') |
| |
| Patch using fake_filesystem_unittest.Patcher |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| If you are using other means of testing like `nose <http://nose2.readthedocs.io>`__, you can do the |
| patching using ``fake_filesystem_unittest.Patcher`` - the class doing the actual work |
| of replacing the filesystem modules with the fake modules in the first two approaches. |
| |
| The easiest way is to just use ``Patcher`` as a context manager: |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import Patcher |
| |
| with Patcher() as patcher: |
| # access the fake_filesystem object via patcher.fs |
| patcher.fs.create_file('/foo/bar', contents='test') |
| |
| # the following code works on the fake filesystem |
| with open('/foo/bar') as f: |
| contents = f.read() |
| |
| You can also initialize ``Patcher`` manually: |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import Patcher |
| |
| patcher = Patcher() |
| patcher.setUp() # called in the initialization code |
| ... |
| patcher.tearDown() # somewhere in the cleanup code |
| |
| Patch using unittest.mock (deprecated) |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| You can also use ``mock.patch()`` to patch the modules manually. This approach will |
| only work for the directly imported modules, therefore it is not suited for testing |
| larger code bases. As the other approaches are more convenient, this one is considered |
| deprecated and will not be described in detail. |
| |
| .. _customizing_patcher: |
| |
| Customizing Patcher and TestCase |
| -------------------------------- |
| Both ``fake_filesystem_unittest.Patcher`` and ``fake_filesystem_unittest.TestCase`` |
| provide a few additional arguments for fine-tuning. These are only needed if |
| patching does not work for some module. |
| |
| .. note:: If you need these arguments in ``PyTest``, you have to |
| use ``Patcher`` directly instead of the ``fs`` fixture. Alternatively, |
| you can add your own fixture with the needed parameters (see |
| ``pytest_plugin.py`` for a possible implementation). |
| |
| modules_to_reload |
| ~~~~~~~~~~~~~~~~~ |
| This allows to pass a list of modules that shall be reloaded, thus allowing |
| to patch modules not imported directly. |
| |
| Pyfakefs automatically patches modules only if they are imported directly, e.g: |
| |
| .. code:: python |
| |
| import os |
| import pathlib.Path |
| |
| The following imports of ``os`` and ``pathlib.Path`` will not be patched by |
| ``pyfakefs``, however: |
| |
| .. code:: python |
| |
| import os as my_os |
| from pathlib import Path |
| |
| .. note:: There is one exception to that: importing ``os.path`` like |
| ``from os import path`` will work, because it is handled by ``pyfakefs`` |
| (see also ``patch_path`` below). |
| |
| If adding the module containing these imports to ``modules_to_reload``, they |
| will be correctly patched. |
| |
| modules_to_patch |
| ~~~~~~~~~~~~~~~~ |
| This also allows patching modules that are not patched out of the box, in |
| this case by adding a fake module implementation for a module name. The |
| argument is a dictionary of fake modules mapped to the names to be faked. |
| This can be used to fake modules imported as another name directly. For the |
| ``os`` import above you could also use: |
| |
| .. code:: python |
| |
| with Patcher(modules_to_patch={'my_os': fake_filesystem.FakeOsModule}): |
| test_something() |
| |
| For the second example (``from pathlib import Path``) the syntax is slightly |
| different: |
| |
| .. code:: python |
| |
| with Patcher(modules_to_patch={'pathlib.Path': MyFakePath}): |
| test_something() |
| |
| This will fake the class ``Path`` inside the module ``pathlib``, if imported |
| as ``Path``. |
| Here is an example of how to implement ``MyFakePath``: |
| |
| .. code:: python |
| |
| class MyFakePath(): |
| """Patches `pathlib.Path` by passing all calls to FakePathlibModule.""" |
| fake_pathlib = None |
| |
| def __init__(self, filesystem): |
| if self.fake_pathlib is None: |
| from pyfakefs.fake_pathlib import FakePathlibModule |
| self.__class__.fake_pathlib = FakePathlibModule(filesystem) |
| |
| def __call__(self, *args, **kwargs): |
| return self.fake_pathlib.Path(*args, **kwargs) |
| |
| def __getattr__(self, name): |
| return getattr(self.fake_pathlib.Path, name) |
| |
| patch_path |
| ~~~~~~~~~~ |
| This is True by default, meaning that modules named ``path`` are patched as |
| ``os.path``. If this clashes with another module of the same name, it can be |
| switched off (and imports like ``from os import path`` will not be patched). |
| |
| |
| additional_skip_names |
| ~~~~~~~~~~~~~~~~~~~~~ |
| This may be used to add modules that shall not be patched. This is mostly |
| used to avoid patching the Python file system modules themselves, but may be |
| helpful in some special situations. |
| |
| use_dynamic_patch |
| ~~~~~~~~~~~~~~~~~ |
| If ``True`` (the default), dynamic patching after setup is used (for example |
| for modules loaded locally inside of functions). |
| Can be switched off if it causes unwanted side effects. |
| |
| Using convenience methods |
| ------------------------- |
| While ``pyfakefs`` can be used just with the standard Python file system |
| functions, there are few convenience methods in ``fake_filesystem`` that can |
| help you setting up your tests. The methods can be accessed via the |
| ``fake_filesystem`` instance in your tests: ``Patcher.fs``, the ``fs`` |
| fixture in PyTest, or ``TestCase.fs``. |
| |
| File creation helpers |
| ~~~~~~~~~~~~~~~~~~~~~ |
| To create files, directories or symlinks together with all the directories |
| in the path, you may use ``create_file()``, ``create_dir()`` and |
| ``create_symlink()``, respectively. |
| |
| ``create_file()`` also allows you to set the file mode and the file contents |
| together with the encoding if needed. Alternatively, you can define a file |
| size without contents - in this case, you will not be able to perform |
| standard I\O operations on the file (may be used to "fill up" the file system |
| with large files). |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import TestCase |
| |
| class ExampleTestCase(TestCase): |
| def setUp(self): |
| self.setUpPyfakefs() |
| |
| def test_create_file(self): |
| file_path = '/foo/bar/test.txt' |
| self.fs.create_file(file_path, contents = 'test') |
| with open(file_path) as f: |
| self.assertEqual('test', f.read()) |
| |
| ``create_dir()`` behaves like ``os.makedirs()``, but can also be used in |
| Python 2. |
| |
| Access to files in the real file system |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| If you want to have read access to real files or directories, you can map |
| them into the fake file system using ``add_real_file()``, |
| ``add_real_directory()`` and ``add_real_paths()``. They take a file path, a |
| directory path, or a list of paths, respectively, and make them accessible |
| from the fake file system. By default, the contents of the mapped files and |
| directories are read only on demand, so that mapping them is relatively |
| cheap. The access to the files is by default read-only, but even even if you |
| add them using ``read_only=False``, the files are written only in the fake |
| system (e.g. in memory). The real files are never changed. |
| |
| ``add_real_file()`` and ``add_real_directory()`` also allow you to map a |
| file or a directory tree into another location in the fake filesystem via the |
| argument ``target_path``. |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import TestCase |
| |
| class ExampleTestCase(TestCase): |
| |
| fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') |
| def setUp(self): |
| self.setUpPyfakefs() |
| # make the file accessible in the fake file system |
| self.fs.add_real_directory(self.fixture_path) |
| |
| def test_using_fixture1(self): |
| with open(os.path.join(self.fixture_path, 'fixture1.txt') as f: |
| # file contents are copied to the fake file system |
| # only at this point |
| contents = f.read() |
| |
| Handling mount points |
| ~~~~~~~~~~~~~~~~~~~~~ |
| Under Linux and MacOS, the root path (``/``) is the only mount point created |
| in the fake file system. If you need support for more mount points, you can add |
| them using ``add_mount_point()``. |
| |
| Under Windows, drives and UNC paths are internally handled as mount points. |
| Adding a file or directory on another drive or UNC path automatically |
| adds a mount point for that drive or UNC path root if needed. Explicitly |
| adding mount points shall not be needed under Windows. |
| |
| A mount point has a separate device ID (``st_dev``) under all systems, and |
| some operations (like ``rename``) are not possible for files located on |
| different mount points. The fake file system size (if used) is also set per |
| mount point. |
| |
| Setting the file system size |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| If you need to know the file system size in your tests (for example for |
| testing cleanup scripts), you can set the fake file system size using |
| ``set_disk_usage()``. By default, this sets the total size in bytes of the |
| root partition; if you add a path as parameter, the size will be related to |
| the mount point (see above) the path is related to. |
| |
| By default, the size of the fake file system is considered infinite. As soon |
| as you set a size, all files will occupy the space according to their size, |
| and you may fail to create new files if the fake file system is full. |
| |
| .. code:: python |
| |
| from fake_filesystem_unittest import TestCase |
| |
| class ExampleTestCase(TestCase): |
| |
| def setUp(self): |
| self.setUpPyfakefs() |
| self.fs.set_disk_usage(100) |
| |
| def test_disk_full(self): |
| with open('/foo/bar.txt', 'w') as f: |
| self.assertRaises(OSError, f.write, 'a' * 200) |
| |
| To get the file system size, you may use ``get_disk_usage()``, which is |
| modeled after ``shutil.disk_usage()``. |