Reputation: 191
I am attempting to write a unit test for a function that calls the open
method on a pathlib.Path
. I am able to successfully mock the open
method without issue, but verifying the function is having the correct behavior is difficult. See the sample code below:
def test_my_function(self):
with patch.object(Path, 'open') as mock_open:
my_function(*args) # This function calls Path.open
When I introspect mock_open
and review the _mock_mock_calls
list, I am unable to find the string path of the file that is being written to. The call history looks like this:
[
call(mode='w'),
call().__enter__(),
call().__enter__().write('<file contents>'),
call().__enter__().flush(),
call().__exit__(None, None, None),
]
Is there a way to test what path is being opened when Path.open
is called?
Upvotes: 10
Views: 2630
Reputation: 1121784
You replaced a method with a mock object. The issue with using a mock object here is that it won't be bound to the Path()
instance. It'll be called, but there is no path back to the Path()
instance (no pun intended).
Use a function to mock out open()
, one that returns a mock_open()
object to track further 'open file' use, functions will be bound when accessed on instances of Path
:
from unittest.mock import patch, mock_open
def test_my_function(self):
opener = mock_open()
def mocked_open(self, *args, **kwargs):
return opener(self, *args, **kwargs)
with patch.object(Path, 'open', mocked_open):
my_function(*args) # This function calls Path.open
Now any Path().open()
call will call the opener
mock, recording all file interactions and the Path()
object on which it was called:
>>> from pathlib import Path
>>> from unittest.mock import patch, mock_open
>>> opener = mock_open()
>>> def mocked_open(self, *args, **kwargs):
... return opener(self, *args, **kwargs)
...
>>> with patch.object(Path, 'open', mocked_open):
... print(Path.open)
... print(Path().open)
... with Path().open() as f:
... f.write('<file contents>')
... f.flush()
...
<function mocked_open at 0x12026f5c0>
<bound method mocked_open of PosixPath('.')>
<MagicMock name='open().flush()' id='4834728928'>
>>> opener.mock_calls
[call(PosixPath('.')),
call().__enter__(),
call().write('<file contents>'),
call().flush(),
call().__exit__(None, None, None)]
Upvotes: 8