Home | History | Annotate | Download | only in pyfakefs
      1 # Copyright 2014 Altera Corporation. All Rights Reserved.
      2 # Copyright 2015 John McGehee
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 
     16 """A base class for unit tests using the :py:class:`pyfakefs` module.
     17 
     18 This class searches `sys.modules` for modules that import the `os`, `glob`,
     19 `shutil`, and `tempfile` modules.
     20 
     21 The `setUp()` method binds these modules to the corresponding fake
     22 modules from `pyfakefs`.  Further, the built in functions `file()` and
     23 `open()` are bound to fake functions.
     24 
     25 The `tearDownPyfakefs()` method returns the module bindings to their original
     26 state.
     27 
     28 It is expected that `setUp()` be invoked at the beginning of the derived
     29 class' `setUp()` method, and `tearDownPyfakefs()` be invoked at the end of the
     30 derived class' `tearDown()` method.
     31 
     32 During the test, everything uses the fake file system and modules.  This means
     33 that even in your test, you can use familiar functions like `open()` and
     34 `os.makedirs()` to manipulate the fake file system.
     35 
     36 This also means existing unit tests that use the real file system can be
     37 retrofitted to use `pyfakefs` by simply changing their base class from
     38 `:py:class`unittest.TestCase` to
     39 `:py:class`pyfakefs.fake_filesystem_unittest.TestCase`.
     40 """
     41 
     42 import sys
     43 import unittest
     44 import doctest
     45 import fake_filesystem
     46 import fake_filesystem_glob
     47 import fake_filesystem_shutil
     48 import fake_tempfile
     49 if sys.version_info < (3,):
     50     import __builtin__ as builtins
     51 else:
     52     import builtins
     53 
     54 import mox3.stubout
     55 
     56 def load_doctests(loader, tests, ignore, module):
     57     '''Load the doctest tests for the specified module into unittest.'''
     58     _patcher = Patcher()
     59     globs = _patcher.replaceGlobs(vars(module))
     60     tests.addTests(doctest.DocTestSuite(module,
     61                                         globs=globs,
     62                                         setUp=_patcher.setUp,
     63                                         tearDown=_patcher.tearDown))
     64     return tests
     65 
     66 
     67 class TestCase(unittest.TestCase):
     68     def __init__(self, methodName='runTest'):
     69         super(TestCase, self).__init__(methodName)
     70         self._stubber = Patcher()
     71         
     72     @property
     73     def fs(self):
     74         return self._stubber.fs
     75     
     76     @property
     77     def patches(self):
     78         return self._stubber.patches
     79         
     80     def setUpPyfakefs(self):
     81         '''Bind the file-related modules to the :py:class:`pyfakefs` fake file
     82         system instead of the real file system.  Also bind the fake `file()` and
     83         `open()` functions.
     84         
     85         Invoke this at the beginning of the `setUp()` method in your unit test
     86         class.
     87         '''
     88         self._stubber.setUp()
     89         self.addCleanup(self._stubber.tearDown)
     90 
     91     
     92     def tearDownPyfakefs(self):
     93         ''':meth:`pyfakefs.fake_filesystem_unittest.setUpPyfakefs` registers the
     94         tear down procedure using :meth:`unittest.TestCase.addCleanup`.  Thus this
     95         method is deprecated, and remains just for backward compatibility.
     96         '''
     97         pass
     98 
     99 class Patcher(object):
    100     '''
    101     Instantiate a stub creator to bind and un-bind the file-related modules to
    102     the :py:mod:`pyfakefs` fake modules.
    103     '''
    104     SKIPMODULES = set([None, fake_filesystem, fake_filesystem_glob,
    105                       fake_filesystem_shutil, fake_tempfile, sys])
    106     '''Stub nothing that is imported within these modules.
    107     `sys` is included to prevent `sys.path` from being stubbed with the fake
    108     `os.path`.
    109     '''
    110     assert None in SKIPMODULES, "sys.modules contains 'None' values; must skip them."
    111     
    112     SKIPNAMES = set(['os', 'glob', 'path', 'shutil', 'tempfile'])
    113         
    114     def __init__(self):
    115         # Attributes set by _findModules()
    116         self._osModules = None
    117         self._globModules = None
    118         self._pathModules = None
    119         self._shutilModules = None
    120         self._tempfileModules = None
    121         self._findModules()
    122         assert None not in vars(self).values(), \
    123                 "_findModules() missed the initialization of an instance variable"
    124         
    125         # Attributes set by _refresh()
    126         self._stubs = None
    127         self.fs = None
    128         self.fake_os = None
    129         self.fake_glob = None
    130         self.fake_path = None
    131         self.fake_shutil = None
    132         self.fake_tempfile_ = None
    133         self.fake_open = None
    134         # _isStale is set by tearDown(), reset by _refresh()
    135         self._isStale = True
    136         self._refresh()
    137         assert None not in vars(self).values(), \
    138                 "_refresh() missed the initialization of an instance variable"
    139         assert self._isStale == False, "_refresh() did not reset _isStale"
    140         
    141     def _findModules(self):
    142         '''Find and cache all modules that import file system modules.
    143         Later, `setUp()` will stub these with the fake file system
    144         modules.
    145         '''
    146         self._osModules = set()
    147         self._globModules = set()
    148         self._pathModules = set()
    149         self._shutilModules = set()
    150         self._tempfileModules = set()
    151         for name, module in set(sys.modules.items()):
    152             if module in self.SKIPMODULES or name in self.SKIPNAMES:
    153                 continue
    154             if 'os' in module.__dict__:
    155                 self._osModules.add(module)
    156             if 'glob' in module.__dict__:
    157                 self._globModules.add(module)
    158             if 'path' in module.__dict__:
    159                 self._pathModules.add(module)
    160             if 'shutil' in module.__dict__:
    161                 self._shutilModules.add(module)
    162             if 'tempfile' in module.__dict__:
    163                 self._tempfileModules.add(module)
    164 
    165     def _refresh(self):
    166         '''Renew the fake file system and set the _isStale flag to `False`.'''
    167         if self._stubs is not None:
    168             self._stubs.SmartUnsetAll()
    169         self._stubs = mox3.stubout.StubOutForTesting()
    170 
    171         self.fs = fake_filesystem.FakeFilesystem()
    172         self.fake_os = fake_filesystem.FakeOsModule(self.fs)
    173         self.fake_glob = fake_filesystem_glob.FakeGlobModule(self.fs)
    174         self.fake_path = self.fake_os.path
    175         self.fake_shutil = fake_filesystem_shutil.FakeShutilModule(self.fs)
    176         self.fake_tempfile_ = fake_tempfile.FakeTempfileModule(self.fs)
    177         self.fake_open = fake_filesystem.FakeFileOpen(self.fs)
    178 
    179         self._isStale = False
    180 
    181     def setUp(self, doctester=None):
    182         '''Bind the file-related modules to the :py:mod:`pyfakefs` fake
    183         modules real ones.  Also bind the fake `file()` and `open()` functions.
    184         '''
    185         if self._isStale:
    186             self._refresh()
    187         
    188         if doctester is not None:
    189             doctester.globs = self.replaceGlobs(doctester.globs)
    190             
    191         if sys.version_info < (3,):
    192             # No file() in Python3
    193             self._stubs.SmartSet(builtins, 'file', self.fake_open)
    194         self._stubs.SmartSet(builtins, 'open', self.fake_open)
    195         
    196         for module in self._osModules:
    197             self._stubs.SmartSet(module,  'os', self.fake_os)
    198         for module in self._globModules:
    199             self._stubs.SmartSet(module,  'glob', self.fake_glob)
    200         for module in self._pathModules:
    201             self._stubs.SmartSet(module,  'path', self.fake_path)
    202         for module in self._shutilModules:
    203             self._stubs.SmartSet(module,  'shutil', self.fake_shutil)
    204         for module in self._tempfileModules:
    205             self._stubs.SmartSet(module,  'tempfile', self.fake_tempfile_)
    206     
    207     def replaceGlobs(self, globs_):
    208         globs = globs_.copy()
    209         if self._isStale:
    210             self._refresh()
    211         if 'os' in globs:
    212             globs['os'] = fake_filesystem.FakeOsModule(self.fs)
    213         if 'glob' in globs:
    214             globs['glob'] = fake_filesystem_glob.FakeGlobModule(self.fs)
    215         if 'path' in globs:
    216             globs['path'] =  fake_filesystem.FakePathModule(self.fs)
    217         if 'shutil' in globs:
    218             globs['shutil'] = fake_filesystem_shutil.FakeShutilModule(self.fs)
    219         if 'tempfile' in globs:
    220             globs['tempfile'] = fake_tempfile.FakeTempfileModule(self.fs)
    221         return globs
    222     
    223     def tearDown(self, doctester=None):
    224         '''Clear the fake filesystem bindings created by `setUp()`.'''
    225         self._isStale = True
    226         self._stubs.SmartUnsetAll()
    227