Home > front end >  Check if Path.exists() was called once with mock.path in a unittest
Check if Path.exists() was called once with mock.path in a unittest

Time:04-22

I need to know if Path.exists() was called. I want to do that in a unittest but allways got the answer that it was not called.

I assume something is wrong with the import path. I know from the docs and some blog-posts that this can be tricky.

This is the "productive" code:

import pathlib

class Foo():
    def __init__(self, file_path: pathlib.Path):
        if not file_path.exists():
            print('Not good.')
        else:
            print('Fine.')

And this is the unittest for it

import unittest
import unittest.mock
import pathlib

import mycode

class MyTest(unittest.TestCase):
    @unittest.mock.patch('pathlib.Path')
    def test_mycode(self, mock_path):
        mypath = pathlib.Path('bar')
        foo = mycode.Foo(mypath)

        mock_path.exists.assert_called_once()

But the error is still

AssertionError: Expected 'exists' to have been called once. Called 0 times.

CodePudding user response:

You can create mock for pathlib.Path using create_autospec helper function. And pass this mock object to the constructor of the Foo class.

Functions or methods being mocked will have their arguments checked to ensure that they are called with the correct signature.

E.g. (Python 3.9.6)

foo.py:

import pathlib


class Foo():
    def __init__(self, file_path: pathlib.Path):
        if not file_path.exists():
            print('Not good.')
        else:
            print('Fine.')

foo_test.py:

import unittest
import pathlib
from unittest.mock import create_autospec
from foo import Foo


class MyTest(unittest.TestCase):
    def test_Foo_file_path_exists(self):
        mock_path = create_autospec(pathlib.Path)
        mock_path.exists.return_value = True
        Foo(mock_path)
        mock_path.exists.assert_called_once()

    def test_Foo_file_path_not_exists(self):
        mock_path = create_autospec(pathlib.Path)
        mock_path.exists.return_value = False
        Foo(mock_path)
        mock_path.exists.assert_called_once()


if __name__ == '__main__':
    unittest.main()

Test result:

Fine.
.Not good.
.
----------------------------------------------------------------------
Ran 2 tests in 0.184s

OK
Name                                     Stmts   Miss  Cover   Missing
----------------------------------------------------------------------
src/stackoverflow/71945781/foo.py            6      0   100%
src/stackoverflow/71945781/foo_test.py      17      0   100%
----------------------------------------------------------------------
TOTAL                                       23      0   100%

CodePudding user response:

The answer of @slideshowp2 is fine and working. And it pointed me to the right solution better fitting to my own MWE.

class MyTest(unittest.TestCase):
    @unittest.mock.patch('pathlib.Path')
    def test_mycode(self, mocked_path):
        foo = mycode.Foo(mocked_path)
        mocked_path.exists.assert_called_once()

The problem with my code was that I instantiated a real object of pathlib.Path. But the key of the solution is to use the mock object mocked_path because this is a surrogate for a real Path object.

In my questions intial code the Foo.__init__() never used the mocked object but the real pathlib.Path.

  • Related