Mocking pathlib.Path I/O methods in a maintainable way which tests functionality not implementation I have a function that constructs a path based on a function of inputs and creates the parent directory.  

When writing a unit test for this function, I want to mock the I/O part of the function, namely the .exists() and .mkdir() methods of the pathlib.Path objects.  However, since both are not called until the path has been constructed, I find myself reconstructing the implementation using a long chain of return_value.__truediv__ accesses, such as below:

import pathlib
import unittest.mock
import datetime


def get(base, timestamp, channel):
    base = pathlib.Path(base)
    path = base / "tofu" / timestamp.strftime("%Y%m%d") / f"{channel:&gt;02d}" / "quinoa.txt"
    if path.exists():
        print(path, "exists")
    else:
        path.parent.mkdir(exist_ok=True, parents=True)
    return path


@unittest.mock.patch("pathlib.Path", autospec=True)
def test_get(pP):
    pP.return_value.__truediv__.return_value.__truediv__.return_value.\
                    __truediv__.return_value.__truediv__.return_value.\
                    exists.return_value = False
    get("quorn", datetime.datetime.now(), 42)
    pP.return_value.__truediv__.return_value.__truediv__.return_value.\
                    __truediv__.return_value.__truediv__.return_value.\
                    parent.mkdir.assert_called_once()


This test passes, but I have several concerns:


It is testing implementation rather than functionality.  If I were to rewrite the second line in get to read path = base / timestamp.strftime("tofu%Y%m%d") / f{channel:&gt;02d} / "quinoa.txt", the functionality would be identical, but the test would fail as I wouldn't be mocking the right value anymore.
It is hard to read and easy to miscount the depth of __truediv__.return_value chain.


Apart from moving the I/O out of get, can I mock this in another way, that does not share those limitations?  Or does the difficulty of writing a good test (with mock) telling me my function is too complex, despite it's small size?
Mocking system calls, especially those with side effects (mkdir()), is difficult and error prone.

I would rather use a temporary directory for test purposes. This approach has the added advantage of really testing your FS manipluation code.

import datetime
import os
import pathlib

from contextlib import contextmanager
from tempfile import TemporaryDirectory


@contextmanager
def temporary_test_dir():
    oldpwd = os.getcwd()
    with TemporaryDirectory("test-path") as td:
        try:
            os.chdir(td)
            yield
        finally:
            os.chdir(oldpwd)


def get(base, timestamp, channel):
    base = pathlib.Path(base)
    path = base / "tofu" / timestamp.strftime("%Y%m%d") / f"{channel:&gt;02d}" / "quinoa.txt"
    if path.exists():
        print(path, "exists")
    else:
        path.parent.mkdir(exist_ok=True, parents=True)
    return path


@temporary_test_dir()
def test_get():
    # All relative FS operations will be made in a temporary directory
    result = get("quorn", datetime.datetime.now(), 42)
    assert result.parent.exists()



I'm not sure what is the point of the tested function, but it doesn't matter for this example's sake.

If you really need to mock the filesystem part, I would suggest using an existing library such as pyfakefs, although I have never had such a need.