Home > other >  Check the value of Path-object when mocking unlink()
Check the value of Path-object when mocking unlink()

Time:02-03

My productive code takes a pathlib.Path instance and creates a second one with modified suffix out of it and delete that second file if it exists.

def productive_code(fp):
    # "foo"   ".wrong"   ".csv" = "foo.wrong.csv"
    fp.with_suffix('.wrong{}'.format(fp.suffix))
    
    fp_wrong.unlink(missing_ok=True)  # >= Python3.8

In the unittest I patch the pathlib.Path.unlink() and I can check if and with which parameters it was called.

But how can I check the "value" of the path object itself? I want to test if the "second" file name was correct created.

import unittest
from unittest import mock
import pathlib

    class TestMy(unittest.TestCase):
        @mock.patch('pathlib.Path.unlink')
        def test_unlink(self, mock_unlink):
            productive_code(pathlib.Path('foo.csv'))
            mock_unlink.assert_called_once_with(missing_ok=True)
            # test "foo.wrong.csv" !!!

CodePudding user response:

I tried to define a side_effect and to mock the Path object itself (using wraps) but to no avail.

But I found this answer and it works fine !
You can use autospec=True to turn

import unittest
from unittest import mock
import pathlib


def productive_code(fp):
    # "foo"   ".wrong"   ".csv" = "foo.wrong.csv"
    fp_wrong = fp.with_suffix('.wrong').with_suffix(fp.suffix)

    fp_wrong.unlink(missing_ok=True)  # >= Python3.8


class TestMy(unittest.TestCase):
    @mock.patch('pathlib.Path.unlink', autospec=True)
    #                                  ^^^^^^^^^^^^^
    def test_unlink(self, mock_unlink):
        productive_code(pathlib.Path('foo.csv'))
        mock_unlink.assert_called_once_with(pathlib.Path("foo.wrong.csv"), missing_ok=True)

This info was not in the mock library documentation but from the "getting started" page which is linked nowhere but in the "Next section" buttons. I'm aghast I'm discovering it just now !

Back on the subject, now that you can check which path gets unlinked, you will notice that your productive_code implementation is wrong : you assumed that calling with_suffix twice would concatenate both, but that's not the case, per the doc :

Return a new path with the suffix changed.

In essence :

>>> from pathlib import Path
>>> Path("a.txt").with_suffix(".mp3")
PosixPath('a.mp3')
>>> Path("a.txt").with_suffix(".mp3").with_suffix(".html")
PosixPath('a.html')
>>> Path("a.txt.mp3.html").with_suffix(".zip")
PosixPath('a.txt.mp3.zip')

Another approach to the testing problem would be this code review's answer :

Mocking system calls, especially those with side effects (mkdir()), is difficult and error prone. [...] 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.

If your testcode goes very wrong, you could damage your system (unlinking files that should not).

PS: nice minimal reproducible example :)

  •  Tags:  
  • Related