Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top