'MagicMock and monkeypatch questions - mock objects lose properties when monkeypatching them
I am trying to mock the content of a file that I read from an sFTP server. Here is the method I want to test, simplified:
def test_target():
try:
fs = SFTPFileSystem(...)
objs = listFolder(fs, "/some_folder")
for obj in objs:
with fs.open(obj["name"]) as f:
file_content = f.read()
... process file_content
finally:
if fs:
fs.ftp.close()
So I started with this approach:
In [12]: mock_fs = MagicMock()
In [13]: mock_fs.open.return_value = io.BytesIO("Hello World".encode("utf-8"))
Works:
In [14]: mock_fs.open().read()
Out[14]: b'Hello World'
However, when I monkeypatch with this mocked object:
monkeypatch.setattr(my_app.my_module, "SFTPFileSystem", mock_fs)
calls to open() in my_module return a MagicMock, not my byte stream.
file_content is reported as <MagicMock name='SFTPFileSystem().open().__enter__().read()' id='...'>
It looks like by calling setattr() it becomes a different instance that does not have the return value set anymore. Why is that?
Similarily, using patch()
with patch("my_app.my_module.SFTPFileSystem", spec=True) as m:
m.ftp = MagicMock()
test_target()
This will throw an exception when I call a method on ftp of my SFTPFileSystem instance in the test target:
E AttributeError: Mock object has no attribute 'ftp'
Oddly, fs is
<NonCallableMagicMock name='SFTPFileSystem()' spec='SFTPFileSystem' id='139762783658544'>.
Why the non-callable form?
What always works reliably is monkeypatching methods in my_module. This has been my work around. I can put each piece of functionality that I need to patch into a separate method, then I can override it. But this approach requires that I refactor my code, which I'd rather avoid.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
