Reputation: 10585
I am writing some unit tests for a module I have. I need to patch open
so when the functions inside the tested module calls open
the mock is used instead of the real open
.
This code works but I think it would break another tests because is not restoring open
to its original value:
class TestCases(unittest.TestCase):
def test_something(self):
from amodule import bmodule
open_mock = mock.MagicMock(spec=open)
bmodule.__builtins__['open'] = open_mock
read_mock = mock.MagicMock()
open_mock.return_value.__enter__.return_value = read_mock
self.assertTrue(bmodule.some_function())
self.assertEqual(open_mock.call_args_list, ['filename1', 'filename2'])
How can I do this using mock.patch
?
Upvotes: 2
Views: 1396
Reputation: 8731
Rather than monkeypatching open
in __builtins__
, you can patch it out in bmodule itself. The benefit of that is that only functions in bmodule will get your patched-out open function.
You can see more detail on this in the mock documentation.
So you can put your version of open in place by using patch.object
as a context manager:
from mock import patch
class TestCases(unittest.TestCase):
def test_something(self):
from amodule import bmodule
open_mock = mock.MagicMock(spec=open)
read_mock = mock.MagicMock()
open_mock.return_value.__enter__.return_value = read_mock
with patch.object(bmodule, 'open', open_mock, create=True):
self.assertTrue(bmodule.some_function())
self.assertEqual(open_mock.call_args_list, ['filename1', 'filename2'])
The with statement guarantees that the patch will be removed when execution leaves the with block. The create=True
part is needed to convince patch that you do intend to create the open
binding in the bmodule
namespace - this is a safety precaution to prevent people from accidentally mocking the wrong name, but in your case it's needed because open lives in __builtins__
but you want to bind it in bmodule
.
Upvotes: 4