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?
-
2The concept `filesystem` is too broad. It could be anyting. What exactly do you want? – Leonardo.Z Oct 30 '13 at 01:31
-
@Leonardo.Z: Any interaction with the file system. I am mostly concerned about **creating**, **opening** and **deleting** files and directories. In other languages the whole file-system can be mocked. – DudeOnRock Oct 30 '13 at 01:36
-
4Would the [tempfile](http://docs.python.org/2/library/tempfile.html) module be of help? – SethMMorton Oct 30 '13 at 02:13
5 Answers
pyfakefs (homepage) does what you want – a fake filesystem; it’s third-party, though that party is Google. See How to replace file-access references for a module under test for discussion of use.
For mocking, unittest.mock is the standard library for Python 3.3+ (PEP 0417); for earlier version see PyPI: mock (for Python 2.5+) (homepage).
Terminology in testing and mocking is inconsistent; using the Test Double terminology of Gerard Meszaros, you’re asking for a “fake”: something that behaves like a filesystem (you can create, open, and delete files), but isn’t the actual file system (in this case it’s in-memory), so you don’t need to have test files or a temporary directory.
In classic mocking, you would instead mock out the system calls (in Python, mock out functions in the os
module, like os.rm
and os.listdir
), but that’s much more fiddly.

- 1
- 1

- 3,239
- 2
- 26
- 27
-
1An important note on [compatibility of pyfakefs](https://github.com/jmcgeheeiv/pyfakefs/#compatibility): "pyfakefs will not work with Python libraries that use C libraries to access the file system. This is because pyfakefs cannot patch the underlying C libraries' file access functions--the C libraries will always access the real file system. For example, pyfakefs will not work with `lxml`. In this case `lxml` must be replaced with a pure Python alternative such as `xml.etree.ElementTree`." – Chris Collett Oct 04 '21 at 15:57
-
Anyone ever got pyfakefs working with behave? For me, all the stuff in `os` does not get patched as soon as I run the script using behave. – Joerg S Aug 12 '22 at 08:40
pytest is gaining a lot of traction, and it can do all of this using tmpdir and monkeypatching (mocking).
You can use the tmpdir
function argument which will provide a temporary directory unique to the test invocation, created in the base temporary directory (which are by default created as sub-directories of the system temporary directory).
import os
def test_create_file(tmpdir):
p = tmpdir.mkdir("sub").join("hello.txt")
p.write("content")
assert p.read() == "content"
assert len(tmpdir.listdir()) == 1
The monkeypatch
function argument helps you to safely set/delete an attribute, dictionary item or environment variable or to modify sys.path
for importing.
import os
def test_some_interaction(monkeypatch):
monkeypatch.setattr(os, "getcwd", lambda: "/")
You can also pass it a function instead of using lambda.
import os.path
def getssh(): # pseudo application code
return os.path.join(os.path.expanduser("~admin"), '.ssh')
def test_mytest(monkeypatch):
def mockreturn(path):
return '/abc'
monkeypatch.setattr(os.path, 'expanduser', mockreturn)
x = getssh()
assert x == '/abc/.ssh'
# You can still use lambda when passing arguments, e.g.
# monkeypatch.setattr(os.path, 'expanduser', lambda x: '/abc')
If your application has a lot of interaction with the file system, then it might be easier to use something like pyfakefs, as mocking would become tedious and repetitive.

- 13,355
- 13
- 76
- 103

- 56,821
- 26
- 143
- 139
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!

- 3,239
- 2
- 26
- 27

- 660
- 8
- 19
Faking or Mocking?
Personally, I find that there are a lot of edge cases in filesystem things (like opening the file with the right permissions, string-vs-binary, read/write mode, etc), and using an accurate fake filesystem can find a lot of bugs that you might not find by mocking. In this case, I would check out the memoryfs
module of pyfilesystem
(it has various concrete implementations of the same interface, so you can swap them out in your code).
Mocking (and without Monkey Patching!):
That said, if you really want to mock, you can do that easily with Python's unittest.mock
library:
import unittest.mock
# production code file; note the default parameter
def make_hello_world(path, open_func=open):
with open_func(path, 'w+') as f:
f.write('hello, world!')
# test code file
def test_make_hello_world():
file_mock = unittest.mock.Mock(write=unittest.mock.Mock())
open_mock = unittest.mock.Mock(return_value=file_mock)
# When `make_hello_world()` is called
make_hello_world('/hello/world.txt', open_func=open_mock)
# Then expect the file was opened and written-to properly
open_mock.assert_called_once_with('/hello/world.txt', 'w+')
file_mock.write.assert_called_once_with('hello, world!')
The above example only demonstrates creating and writing to files via mocking the open()
method, but you could just as easily mock any method.

- 31,947
- 10
- 111
- 111

- 7,423
- 4
- 41
- 57
-
-
@antony-hatchkins @weberc2 You will need to add the `__enter__()` and `__exit__()` functions to `open_mock` to use the `with open_func()` syntax: See https://realpython.com/python-with-statement/#the-with-statement-approach for details. Also see https://docs.python.org/3/library/unittest.mock.html#mock-open or my answer for an off-the-shelf version of this – Sarah Messer Jul 15 '22 at 15:01
-
@SarahMesser As a matter of fact, I'm aware of how it can be done. I just want the answer to be complete and self-sufficient. Thanks anyway. ) – Antony Hatchkins Jul 15 '22 at 16:02
The standard unittest.mock
library has a mock_open()
function which provides basic mocking of the file system.
Benefits: It's part of the standard library, and inherits the various features of Mocks, including checking call parameters & usage.
Drawbacks: It doesn't maintain filesystem state the way pytest or pyfakefs or mockfs does, so it's harder to test functions that do R/W interactions or interact with multiple files simultaneously.

- 3,592
- 1
- 26
- 43