Automatically find and patch file functions and modules

mrbean-bremen edited this page Nov 2, 2017 · 2 revisions

The fake_filesystem_unittest module automatically finds all real file functions and modules, and stubs them out with the fake file system functions and modules. The pyfakefs source code contains files demonstrate this usage model:

  • example.py is the software under test. In production, it uses the real file system.
  • example_test.py tests example.py. During testing, the pyfakefs fake file system is used byexample_test.py and example.py alike.

Software Under Test

example.py contains a few functions that manipulate files. For instance:

def create_file(path):
    '''Create the specified file and add some content to it.  Use the open()
    built in function.

    For example, the following file operations occur in the fake file system.
    In the real file system, we would not even have permission to write /test:

    >>> os.path.isdir('/test')
    False
    >>> os.mkdir('/test')
    >>> os.path.isdir('/test')
    True
    >>> os.path.exists('/test/file.txt')
    False
    >>> create_file('/test/file.txt')
    >>> os.path.exists('/test/file.txt')
    True
    >>> with open('/test/file.txt') as f:
    ...     f.readlines()
    ["This is test file '/test/file.txt'.\\n", 'It was created using the open() function.\\n']
    '''
    with open(path, 'w') as f:
        f.write("This is test file '{}'.\n".format(path))
        f.write("It was created using the open() function.\n")

No functional code in example.py even hints at a fake file system. In production, create_file() invokes the real file functions open() and write().

Unit Tests and Doctests

example_test.py contains unit tests for example.py. example.py contains the doctests, as you can see above.

Module fake_filesystem_unittest contains code that finds all real file functions and modules, and stubs these out with the fake file system functions and modules:

import os
import unittest
from pyfakefs import fake_filesystem_unittest
# The module under test is example:
import example

Doctests

example_test.py defines load_tests(), which runs the doctests in example.py:

def load_tests(loader, tests, ignore):
    '''Load the pyfakefs/example.py doctest tests into unittest.'''
    return fake_filesystem_unittest.load_doctests(loader, tests, ignore, example)

Everything, including all imported modules and the test, is stubbed out with the fake filesystem. Thus you can use familiar file functions like os.mkdir() as part of your test fixture and they too will operate on the fake file system.

Unit Test Class

Next comes the unittest test class. This class is derived from fake_filesystem_unittest.TestCase, which is in turn derived from unittest.TestClass:

class TestExample(fake_filesystem_unittest.TestCase):

    def setUp(self):
        self.setUpPyfakefs()

    def tearDown(self):
        # It is no longer necessary to add self.tearDownPyfakefs()
        pass

    def test_create_file(self):
        '''Test example.create_file()'''
        # The os module has been replaced with the fake os module so all of the
        # following occurs in the fake filesystem.
        self.assertFalse(os.path.isdir('/test'))
        os.mkdir('/test')
        self.assertTrue(os.path.isdir('/test'))

        self.assertFalse(os.path.exists('/test/file.txt'))
        example.create_file('/test/file.txt')
        self.assertTrue(os.path.exists('/test/file.txt'))

    ...

Just add self.setUpPyfakefs() in setUp(). You need add nothing to tearDown(). Write your tests as usual. From self.setUpPyfakefs() to the end of your tearDown() method, all file operations will use the fake file system.

Convenient Helper Methods

Once setUpPyfakefs() has been executed, your test is using the fake file system. Now you can use the familiar Python standard file interface methods like os.makedirs() and open() to set up your test fixture. In addition to the Python standard file methods, pyfakefs also provides helper methods you can use to set up your test fixture even more easily.

fs Reference to the Fake File System and CreateFile()

setUpPyfakefs() defines attribute fs, a reference to the fake file system. This gives you access to the pyfakefs file system methods and attributes. The most useful of these is CreateFile(). It creates a file along with its requisite directories, then adds content to the file, all in one convenient method:

class TestExample(pyfakefs.fake_filesystem_unittest.TestCase):
    def setUp(self):
        self.setUpPyfakefs()
        self.fs.CreateFile('/test/lots/of/nonexistent/directories/full.txt',
                           contents='First line\n'    # No comma here; a common mistake!
                                    'Second Line\n')

Copy a File from the Real File System to the Fake File System

Often you have template, data or configuration files that are part of the software under test, so you need them present in the fake file system. The class fake_filesystem.FakeFileSystem provides the methods add_real_file(), add_real_directory() and add_real_paths(), which make files and directories from the real file system accessible in the fake file system by copying them there on demand (e.g. on accessing them). The access is read-only be default.

For example, the following code allows to access the real file templates/form.txt in the module directory in the same location in the fake file system:

class TestExample(pyfakefs.fake_filesystem_unittest.TestCase):
    templates_dirname = os.path.join(os.path.dirname(__file__), 'templates')
    def setUp(self):
        self.setUpPyfakefs()
        # make the file accessible in the fake file system
        self.fs.add_real_file(os.path.join(self.templates_dirname, 'form.txt'))

    def testSomething(self):
        with open(os.path.join(self.templates_dirname, 'form.txt') as f:
            # file contents are copied to the fake file system only at this point
            contents = f.read()
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.