Search code examples
pythonmockingpython-mock

How to mock a Python system attribute like os.pathsep?


I am trying to modify a test to handle both Linux and Windows. The code being tested uses os.pathsep. Example:

def path_split(pth):
    if os.pathsep in pth:
        return pth.split(os.pathsep)
    else:
        return [pth]

If I run the following test on Linux using a colon, it works:

class PathsepTest(TestCase):

    def test_path_split(self):
        result = path_split("foo:bar")
        assert result == ["foo", "bar"]

However, if I try to mock os.pathsep to return the Windows path separator (;)

class PathsepTest(TestCase):

    def test_path_split(self):
        windows_pathsep = ";"
        with patch.object(os, "pathsep", return_value=windows_pathsep):
            result = path_split("foo;bar")
            assert result == ["foo", "bar"]

it fails with

    def path_split(pth):
>       if os.pathsep in pth:
E       TypeError: 'in <string>' requires string as left operand, not MagicMock

For a simpler function

def get_pathsep():
    return os.pathsep

the corresponding test fails if I do

    def test_get_pathsep(self):
        windows_pathsep = ";"
        with patch.object(os, "pathsep", return_value=windows_pathsep):
            result = get_pathsep()
            assert result == windows_pathsep

but passes if I do

            assert result.return_value == windows_pathsep

Any suggestions would be welcome.


Solution

  • mock.patch replace an object by another, default is to replace with a MagicMock.

    So patch.object(os, "pathsep", return_value=":"), replace os.pathsep by a MagicMock. Then return_value specify the behavior when you call the mock object (ie. os.pathsep.__call__)

    >>> with mock.patch("os.pathsep", return_value=";"):
    ...     print(os.pathsep())   # os.pathsep has been replaced by a callable
    ... 
    ;
    

    But os.pathsep isn't a callable object, it is str. According to the documentation, you can simply replace the orginal object by another with the new argument:

    >>> with mock.patch("os.pathsep", new=";"):
    ...     print(os.pathsep)
    ... 
    ;