diff options
author | Robert Collins <rbtcollins@hp.com> | 2015-07-17 20:08:45 +1200 |
---|---|---|
committer | Robert Collins <rbtcollins@hp.com> | 2015-07-17 21:09:21 +1200 |
commit | e9db0161fc11eceba189a0cc161deefce57529a8 (patch) | |
tree | ab0e6fc154ec00300d51c7689b777164d0ae1c9c | |
parent | 5c57dd2b9ef71366f302f91693a2e5c290aa473b (diff) | |
download | mock-e9db0161fc11eceba189a0cc161deefce57529a8.tar.gz |
Issue #21750: mock_open.read_data can now be read from each instance, as it
could in Python 3.3.
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | mock/mock.py | 52 | ||||
-rw-r--r-- | mock/tests/testmock.py | 5 | ||||
-rw-r--r-- | mock/tests/testwith.py | 18 |
4 files changed, 64 insertions, 17 deletions
@@ -1,3 +1,9 @@ +Library +------- + +- Issue #21750: mock_open.read_data can now be read from each instance, as it + could in Python 3.3. + - Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely. Patch from Nicola Palumbo and Laurent De Buyst. diff --git a/mock/mock.py b/mock/mock.py index cd90dea..12016d4 100644 --- a/mock/mock.py +++ b/mock/mock.py @@ -2453,6 +2453,7 @@ def _iterate_read_data(read_data): for line in data_as_list: yield line + def mock_open(mock=None, read_data=''): """ A helper function to create a mock to replace the use of `open`. It works @@ -2495,21 +2496,42 @@ def mock_open(mock=None, read_data=''): if mock is None: mock = MagicMock(name='open', spec=open) - handle = MagicMock(spec=file_spec) - handle.__enter__.return_value = handle - - _data = _iterate_read_data(read_data) - - handle.write.return_value = None - handle.read.return_value = None - handle.readline.return_value = None - handle.readlines.return_value = None - - handle.read.side_effect = _read_side_effect - handle.readline.side_effect = _readline_side_effect() - handle.readlines.side_effect = _readlines_side_effect - - mock.return_value = handle + def make_handle(*args, **kwargs): + # Arg checking is handled by __call__ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return ''.join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + _check_and_set_parent(mock, handle, None, '()') + return handle + + mock.side_effect = make_handle return mock diff --git a/mock/tests/testmock.py b/mock/tests/testmock.py index 4be8779..88f94f3 100644 --- a/mock/tests/testmock.py +++ b/mock/tests/testmock.py @@ -1436,6 +1436,11 @@ class MockTest(unittest.TestCase): self.assertEqual(m.mock_calls, [call.__int__(), call.__float__()]) self.assertEqual(m.method_calls, []) + def test_mock_open_reuse_issue_21750(self): + mocked_open = mock.mock_open(read_data='data') + f1 = mocked_open('a-name') + f2 = mocked_open('another-name') + self.assertEqual(f1.read(), f2.read()) def test_mock_parents(self): for Klass in Mock, MagicMock: diff --git a/mock/tests/testwith.py b/mock/tests/testwith.py index 25d706f..11e97bb 100644 --- a/mock/tests/testwith.py +++ b/mock/tests/testwith.py @@ -146,7 +146,6 @@ class TestMockOpen(unittest.TestCase): def test_mock_open_context_manager(self): mock = mock_open() - handle = mock.return_value with patch('%s.open' % __name__, mock, create=True): with open('foo') as f: f.read() @@ -154,8 +153,23 @@ class TestMockOpen(unittest.TestCase): expected_calls = [call('foo'), call().__enter__(), call().read(), call().__exit__(None, None, None)] self.assertEqual(mock.mock_calls, expected_calls) - self.assertIs(f, handle) + # mock_open.return_value is no longer static, because + # readline support requires that it mutate state + def test_mock_open_context_manager_multiple_times(self): + mock = mock_open() + with patch('%s.open' % __name__, mock, create=True): + with open('foo') as f: + f.read() + with open('bar') as f: + f.read() + + expected_calls = [ + call('foo'), call().__enter__(), call().read(), + call().__exit__(None, None, None), + call('bar'), call().__enter__(), call().read(), + call().__exit__(None, None, None)] + self.assertEqual(mock.mock_calls, expected_calls) def test_explicit_mock(self): mock = MagicMock() |