DudeOnRock DudeOnRock - 21 days ago 9
Python Question

How do I mock the filesystem in python unit tests

Is there a standard way (without installing third party libraries) to do cross platform filesystem mocking in python? If I have to go with a third party library, which library is the standard?

Answer

The standard mocking framework in Python 3.3+ is unittest.mock; you can use this for the filesystem or anything else.

You could also simply hand roll it by mocking via monkey patching:

A trivial example:

import os.path
os.path.isfile = lambda path: path == '/path/to/testfile'

A bit more full (untested):

import classtobetested                                                                                                                                                                                      
import unittest                                                                                                                                                                                             

import contextlib                                                                                                                                                                                           

@contextlib.contextmanager                                                                                                                                                                                  
def monkey_patch(module, fn_name, patch):                                                                                                                                                                   
    unpatch = getattr(module, fn_name)                                                                                                                                                                      
    setattr(module, fn_name)                                                                                                                                                                                
    try:                                                                                                                                                                                                    
        yield                                                                                                                                                                                               
    finally:                                                                                                                                                                                                
        setattr(module, fn_name, unpatch)                                                                                                                                                                   


class TestTheClassToBeTested(unittest.TestCase):                                                                                                                                                              
    def test_with_fs_mocks(self):                                                                                                                                                                           
        with monkey_patch(classtobetested.os.path,                                                                                                                                                          
                          'isfile',                                                                                                                                                                         
                          lambda path: path == '/path/to/file'):                                                                                                                                            
            self.assertTrue(classtobetested.testable())                 

In this example, the actual mocks are trivial, but you could back them with something that has state so that can represent filesystem actions, such as save and delete. Yes, this is all a bit ugly since it entails replicating/simulating basic filesystem in code.

Note that you can't monkey patch python builtins. That being said...

For earlier versions, if at all possible use a third party library, I'd go with Michael Foord's awesome Mock, which is now unittest.mock in the standard library since 3.3+ thanks to PEP 0417, and you can get it on PyPI for Python 2.5+. And, it can mock builtins!