Justin Reddick
Justin Reddick

Reputation: 501

Is it possible to mock os.scandir and its attributes?

for entry in os.scandir(document_dir)
    if os.path.isdir(entry):
    # some code goes here
    else:
        # else the file needs to be in a folder
        file_path = entry.path.replace(os.sep, '/')

I am having trouble mocking os.scandir and the path attribute within the else statement. I am not able to mock the mock object's property I created in my unit tests.

with patch("os.scandir") as mock_scandir:
    # mock_scandir.return_value = ["docs.json", ]
    # mock_scandir.side_effect = ["docs.json", ]
    # mock_scandir.return_value.path = PropertyMock(return_value="docs.json")

These are all the options I've tried. Any help is greatly appreciated.

Upvotes: 2

Views: 850

Answers (1)

MrBean Bremen
MrBean Bremen

Reputation: 16815

It depends on what you realy need to mock. The problem is that os.scandir returns entries of type os.DirEntry. One possibility is to use your own mock DirEntry and implement only the methods that you need (in your example, only path). For your example, you also have to mock os.path.isdir. Here is a self-contained example for how you can do this:

import os
from unittest.mock import patch


def get_paths(document_dir):
    # example function containing your code
    paths = []
    for entry in os.scandir(document_dir):
        if os.path.isdir(entry):
            pass
        else:
            # else the file needs to be in a folder
            file_path = entry.path.replace(os.sep, '/')
            paths.append(file_path)
    return paths


class DirEntry:
    def __init__(self, path):
        self.path = path

    def path(self):
        return self.path


@patch("os.scandir")
@patch("os.path.isdir")
def test_sut(mock_isdir, mock_scandir):
    mock_isdir.return_value = False
    mock_scandir.return_value = [DirEntry("docs.json")]
    assert get_paths("anydir") == ["docs.json"]

Depending on your actual code, you may have to do more.

If you want to patch more file system functions, you may consider to use pyfakefs instead, which patches the whole file system. This will be overkill for a single test, but can be handy for a test suite relying on file system functions.

Disclaimer: I'm a contributor to pyfakefs.

Upvotes: 3

Related Questions