pythonpytestpython-unittestpython-unittest.mockpytest-mock

How to mock open file and raise if path does not exists?


I'am facing a problem when testing a function that reads the first line of a file and raises an Exception when the path of the file doesn't exist.

Current code:

from unittest.mock import patch, mock_open
from pytest import raises
from os.path import exists

def read_from_file(file_path):
    if not exists(file_path):
        raise Exception("File does not exists!")
    with open(file_path, "r") as f:
        return f.read().splitlines()[0]

@patch("builtins.open", new_callable=mock_open, read_data="Correct string\nWrong string\nWrong string")
@patch("os.path.exists", return_value=True)
def test_read_file_and_returns_the_correct_string_with_multiple_lines(mock_os, mock_file):
    result = read_from_file("xyz")
    mock_file.assert_called_once_with("xyz", "r")
    assert result == "Correct string"

@patch("builtins.open", new_callable=mock_open, read_data="Correct string")
@patch("os.path.exists", return_value=False)
def test_throws_exception_when_file_doesnt_exist(mock_os, mock_file):
    with raises(Exception):
        read_from_file("xyz")

The decorators @patch("os.path.exists", return_value=True) and @patch("os.path.exists", return_value=False) seems to have no effect in both tests.

How can I mock the existence of a file?


Solution

  • The error arises because the read_from_file directly imports exists from os.path and uses it, but the mock targets os.path.exists, which does not affect the direct import. Hence, the real function gets executed instead of the mock.

    Change read_from_file to this:

    import os.path
    
    def read_from_file(file_path):
        if not os.path.exists(file_path):
            raise Exception("File does not exist!")
        with open(file_path, "r") as f:
            return f.read().splitlines()[0]