pax_global_header00006660000000000000000000000064136456232160014522gustar00rootroot0000000000000052 comment=3ce2bc94edb94eb78dded2960538f9ae8e62f448 nivbend-mock-open-deb644f/000077500000000000000000000000001364562321600155355ustar00rootroot00000000000000nivbend-mock-open-deb644f/.gitignore000066400000000000000000000001001364562321600175140ustar00rootroot00000000000000# Byte-compiled files. *.py[co] build/ dist/ *.egg-info/ .tox/ nivbend-mock-open-deb644f/.travis.yml000066400000000000000000000002721364562321600176470ustar00rootroot00000000000000language: python python: - "2.7" - "3.4" - "3.5" - "3.6" - "3.7" - "3.7-dev" - "3.8-dev" - "nightly" install: - pip install . script: python -m unittest mock_open.test nivbend-mock-open-deb644f/.version000066400000000000000000000000061364562321600172170ustar00rootroot000000000000001.4.0 nivbend-mock-open-deb644f/LICENSE000066400000000000000000000020711364562321600165420ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Niv Ben-David Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nivbend-mock-open-deb644f/MANIFEST.in000066400000000000000000000000631364562321600172720ustar00rootroot00000000000000include README.md include .version include LICENSE nivbend-mock-open-deb644f/README.md000066400000000000000000000044511364562321600170200ustar00rootroot00000000000000mock-open ========= [![PyPI version](https://badge.fury.io/py/mock-open.svg)](https://pypi.python.org/pypi/mock-open/) [![Build Status](https://travis-ci.org/nivbend/mock-open.svg?branch=master)](https://travis-ci.org/nivbend/mock-open) [![GitHub license](https://img.shields.io/github/license/nivbend/mock-open.svg)](https://github.com/nivbend/mock-open/blob/master/LICENSE) [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/nivbend/mock-open/graphs/commit-activity) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) A better mock for file I/O. Install ------- ``` $ pip install mock-open ``` class `MockOpen` ---------------- The `MockOpen` class should work as a stand-in replacement for [`mock.mock_open`][mock-open] with some added features (though it tries to conform to how the builtin `open` works where the two differ): * Multiple file support, including a mapping-like access to file mocks by path: ```python from mock_open import MockOpen mock_open = MockOpen() mock_open["/path/to/file"].read_data = "Data from a fake file-like object" mock_open["/path/to/bad_file"].side_effect = IOError() ``` You can also configure behavior via the regular `mock` library API: ```python mock_open = MockOpen() mock_open.return_value.write.side_effect = IOError() ``` * Persistent file contents between calls to `open`: ```python with patch("builtins.open", MockOpen()): with open("/path/to/file", "w") as handle: handle.write("Some text") with open("/path/to/file", "r") as handle: assert "Some text" == handle.read() ``` * All the regular file operations: `read`, `readline`, `readlines`, `write`, `writelines`, `seek`, `tell`. Acknowledgements ---------------- This library uses modified versions of tests from the [CPython source code][CPython] as part of its test suite. The original tests are licensed under the [PSF license agreement][PSF License] and are copyright of the Python Software Foundation. [mock-open]: http://docs.python.org/library/unittest.mock.html#mock-open [CPython]: https://github.com/python/cpython [PSF License]: https://docs.python.org/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python nivbend-mock-open-deb644f/setup.py000066400000000000000000000033261364562321600172530ustar00rootroot00000000000000"""Setuptools setup script for the package.""" from setuptools import setup, find_packages import sys def _get_version(): # pylint: disable=missing-docstring with open('.version') as version: return version.read().rstrip("\n") def _get_description(): # pylint: disable=missing-docstring with open('README.md') as readme: return readme.read() DEPENDENCIES = [] if sys.version_info < (3, 3): DEPENDENCIES.append('mock') setup( name='mock-open', version=_get_version(), description='A better mock for file I/O', long_description=_get_description(), long_description_content_type='text/markdown', url='http://github.com/nivbend/mock-open', author='Niv Ben-David', author_email='nivbend@gmail.com', license='MIT', packages=find_packages('src'), package_dir={ '': 'src', }, test_suite='mock_open.test', install_requires=DEPENDENCIES, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Topic :: Software Development', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Testing', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', ], keywords=' '.join([ 'testing', 'unittest', 'test', 'mock', 'mocking', 'patch', 'patching', 'stubs', 'fakes', 'doubles' ]), ) nivbend-mock-open-deb644f/src/000077500000000000000000000000001364562321600163245ustar00rootroot00000000000000nivbend-mock-open-deb644f/src/mock_open/000077500000000000000000000000001364562321600202765ustar00rootroot00000000000000nivbend-mock-open-deb644f/src/mock_open/__init__.py000066400000000000000000000000771364562321600224130ustar00rootroot00000000000000"""A better mock for file I/O.""" from .mocks import MockOpen nivbend-mock-open-deb644f/src/mock_open/mocks.py000066400000000000000000000166731364562321600220010ustar00rootroot00000000000000"""Mock classes for open() and the file type.""" import sys from os import SEEK_SET, SEEK_END from io import TextIOWrapper try: # pylint: disable=no-name-in-module from unittest.mock import Mock, NonCallableMock, DEFAULT except ImportError: from mock import Mock, NonCallableMock, DEFAULT if sys.version_info < (3, 0): try: from cStringIO import StringIO, StringIO as BytesIO except ImportError: from StringIO import StringIO, StringIO as BytesIO else: from io import StringIO, BytesIO class FileLikeMock(NonCallableMock): """Acts like a file object returned from open().""" def __init__(self, name=None, read_data='', *args, **kws): kws.update({'spec': TextIOWrapper, }) super(FileLikeMock, self).__init__(*args, **kws) self.mode = None self.__is_closed = False self.read_data = read_data self.close.side_effect = self._close self.__enter__ = Mock(side_effect=self._enter) self.__exit__ = Mock(side_effect=self._exit) if name is not None: self.name = name @property def closed(self): # pylint: disable=missing-docstring return self.__is_closed @property def read_data(self): """Bypass read function to access the contents of the file. This property should be used for testing purposes. """ return self.__contents.getvalue() @read_data.setter def read_data(self, contents): # pylint: disable=missing-docstring # pylint: disable=attribute-defined-outside-init if isinstance(contents, str): self.__contents = StringIO() else: self.__contents = BytesIO() # Constructing a cStrinIO object with the input string would result # in a read-only object, so we write the contents after construction. self.__contents.write(contents) # Set tell/read/write/etc side effects to access the new contents # object. self.tell._mock_wraps = self.__contents.tell self.seek._mock_wraps = self.__contents.seek self.read._mock_wraps = self.__contents.read self.readline._mock_wraps = self.__contents.readline self.readlines._mock_wraps = self.__contents.readlines self.write._mock_wraps = self.__contents.write self.writelines._mock_wraps = self.__contents.writelines def __iter__(self): return iter(self.__contents) def __next__(self): current_position = self.__contents.tell() self._reset_position(0, SEEK_END) eof = self.__contents.tell() if eof <= current_position: raise StopIteration() self._reset_position(current_position) return self.readline() if sys.version_info < (3, 0): def next(self): return self.__next__() def set_properties(self, path, mode): """Set file's properties (name and mode). This function is also in charge of swapping between textual and binary streams. """ if mode is None: mode = 'r' self.name = path self.mode = mode if 'b' in self.mode: if not isinstance(self.read_data, bytes): self.read_data = bytes(self.read_data, encoding='utf8') else: if not isinstance(self.read_data, str): self.read_data = str(self.read_data, encoding='utf8') def reset_mock(self, visited=None): """Reset the default tell/read/write/etc side effects.""" # In some versions of the mock library, `reset_mock` takes an argument # and in some it doesn't. We try to handle all situations. if visited is not None: super(FileLikeMock, self).reset_mock(visited) else: super(FileLikeMock, self).reset_mock() # Reset contents and tell/read/write/close side effects. self.read_data = '' self.close.side_effect = self._close def _reset_position(self, position=0, whence=SEEK_SET): """A shortcut to `_contents`'s `seek` for internal use.""" self.__contents.seek(position, whence) def _enter(self): """Reset the position in buffer according to the mode whenever entering context.""" if 'a' in self.mode: self._reset_position(0, SEEK_END) else: self._reset_position() return self def _exit(self, exception_type, exception, traceback): """Close file when exiting context.""" # pylint: disable=unused-argument self.close() def _close(self): """Mark file as closed (used for side_effect).""" self.__is_closed = True return DEFAULT class MockOpen(Mock): """A mock for the open() builtin function.""" def __init__(self, read_data='', *args, **kws): kws.update({'spec': open, 'name': open.__name__, }) super(MockOpen, self).__init__(*args, **kws) self.__files = {} self.__read_data = read_data def __call__(self, path, mode=None, *args, **kws): original_side_effect = self._mock_side_effect if path in self.__files: self._mock_return_value = self.__files[path] self._mock_side_effect = self._mock_return_value.side_effect try: if mode is not None: child = super(MockOpen, self).__call__(path, mode, *args, **kws) else: child = super(MockOpen, self).__call__(path, *args, **kws) finally: # Reset the side effect after each call so that the next call to # open() won't cause the same side_effect. self._mock_side_effect = original_side_effect # Consecutive calls to open() set `return_value` to the last file mock # created. If the paths differ (and child isn't a newly-created mock, # evident by its name attribute being unset) we create a new file mock # instead of returning the previous one. if not isinstance(child.name, Mock) and path != child.name: child = self._get_child_mock(_new_name='()', name=path) self.__files[path] = child child.set_properties(path, mode) if path not in self.__files: self.__files[path] = child # Each call to `open` should reset the position in the file according to the mode. if mode and 'a' in mode: self.__files[path]._reset_position(0, SEEK_END) else: self.__files[path]._reset_position() self._mock_return_value = child return child def __getitem__(self, path): return self.__files.setdefault(path, self._get_child_mock(name=path)) def __setitem__(self, path, value): value.__enter__ = lambda self: self value.__exit__ = lambda self, *args: None self.__files[path] = value def reset_mock(self, visited=None): # See comment in `FileLikeMock.reset_mock`. if visited is not None: super(MockOpen, self).reset_mock(visited) else: super(MockOpen, self).reset_mock() self.__files = {} self.__read_data = '' def _get_child_mock(self, **kws): """Create a new FileLikeMock instance. The new mock will inherit the parent's side_effect and read_data attributes. """ kws.update({ '_new_parent': self, 'side_effect': self._mock_side_effect, 'read_data': self.__read_data, }) return FileLikeMock(**kws) nivbend-mock-open-deb644f/src/mock_open/test/000077500000000000000000000000001364562321600212555ustar00rootroot00000000000000nivbend-mock-open-deb644f/src/mock_open/test/__init__.py000066400000000000000000000002411364562321600233630ustar00rootroot00000000000000# pylint: disable=missing-docstring # pylint: disable=wildcard-import from .test_mocks import * from .cpython.testmock import * from .cpython.testwith import * nivbend-mock-open-deb644f/src/mock_open/test/cpython/000077500000000000000000000000001364562321600227415ustar00rootroot00000000000000nivbend-mock-open-deb644f/src/mock_open/test/cpython/__init__.py000066400000000000000000000000001364562321600250400ustar00rootroot00000000000000nivbend-mock-open-deb644f/src/mock_open/test/cpython/testmock.py000066400000000000000000000104141364562321600251440ustar00rootroot00000000000000# This file was adapted from CPython's `Lib/unittest/test/testmock/testmock.py` which is licensed # under the PSF license agreement. # # In accordance with the license, these are the major changes made to file: # * Any tests or code not directly used to test `unittest.mock.mock_open` was deleted. # * Instead of using `unittest.mock.mock_open`, the mock-open library's `MockOpen` was imported # instead as `mock_open` to keep changes to the test code minimal. # * Where there were functional differences between `unittest.mock.mock_open` and `MockOpen` the # tests were modified to accomodate the latter rather than the former. These were mostly around # `MockOpen`'s constructor requiring a "path" argument and `FileLikeMock`'s `__exit__` calling # `close` on the underlying file-like object. # # Copyright © 2001-2020 Python Software Foundation; All Rights Reserved. import sys import tempfile import unittest from mock_open import MockOpen as mock_open try: # pylint: disable=no-name-in-module from unittest.mock import MagicMock, patch except ImportError: from mock import MagicMock, patch # bpo-35330 was merged backported from Python v3.8.0 to v3.7.2 and v3.6.8. # Prior to that, side effects and wrapped objects didn't play nicely. HAS_BPO_35330 = (3, 7, 2) <= sys.version_info or (3, 6, 8) <= sys.version_info < (3, 7, 0) class MockTest(unittest.TestCase): def test_reset_mock_on_mock_open_issue_18622(self): a = mock_open() a.reset_mock() def test_mock_open_reuse_issue_21750(self): mocked_open = mock_open(read_data='data') f1 = mocked_open('a-name') f1_data = f1.read() f2 = mocked_open('another-name') f2_data = f2.read() self.assertEqual(f1_data, f2_data) def test_mock_open_dunder_iter_issue(self): # Test dunder_iter method generates the expected result and # consumes the iterator. mocked_open = mock_open(read_data='Remarkable\nNorwegian Blue') f1 = mocked_open('a-name') lines = [line for line in f1] self.assertEqual(lines[0], 'Remarkable\n') self.assertEqual(lines[1], 'Norwegian Blue') self.assertEqual(list(f1), []) def test_mock_open_using_next(self): mocked_open = mock_open(read_data='1st line\n2nd line\n3rd line') f1 = mocked_open('a-name') line1 = next(f1) line2 = f1.__next__() lines = [line for line in f1] self.assertEqual(line1, '1st line\n') self.assertEqual(line2, '2nd line\n') self.assertEqual(lines[0], '3rd line') self.assertEqual(list(f1), []) with self.assertRaises(StopIteration): next(f1) def test_mock_open_next_with_readline_with_return_value(self): mopen = mock_open(read_data='foo\nbarn') mopen.return_value.readline.return_value = 'abc' self.assertEqual('abc', next(mopen('/some/file'))) @unittest.skip('mock-open: Covered by other tests') def test_mock_open_write(self): # Test exception in file writing write() mock_namedtemp = mock_open(MagicMock(name='JLV')) with patch('tempfile.NamedTemporaryFile', mock_namedtemp): mock_filehandle = mock_namedtemp.return_value mock_write = mock_filehandle.write mock_write.side_effect = OSError('Test 2 Error') def attempt(): tempfile.NamedTemporaryFile().write('asd') self.assertRaises(OSError, attempt) @unittest.skipUnless(HAS_BPO_35330, 'mock-open: Requires bpo-35330') def test_mock_open_alter_readline(self): mopen = mock_open(read_data='foo\nbarn') mopen.return_value.readline.side_effect = lambda *args:'abc' first = mopen('/some/file').readline() second = mopen('/some/file').readline() self.assertEqual('abc', first) self.assertEqual('abc', second) def test_mock_open_after_eof(self): # read, readline and readlines should work after end of file. _open = mock_open(read_data='foo') h = _open('bar') h.read() self.assertEqual('', h.read()) self.assertEqual('', h.read()) self.assertEqual('', h.readline()) self.assertEqual('', h.readline()) self.assertEqual([], h.readlines()) self.assertEqual([], h.readlines()) nivbend-mock-open-deb644f/src/mock_open/test/cpython/testwith.py000066400000000000000000000204601364562321600251700ustar00rootroot00000000000000# This file was adapted from CPython's `Lib/unittest/test/testmock/testwith.py` which is licensed # under the PSF license agreement. # # In accordance with the license, these are the major changes made to file: # * Any tests or code not directly used to test `unittest.mock.mock_open` was deleted. # * Instead of using `unittest.mock.mock_open`, the mock-open library's `MockOpen` was imported # instead as `mock_open` to keep changes to the test code minimal. # * Where there were functional differences between `unittest.mock.mock_open` and `MockOpen` the # tests were modified to accomodate the latter rather than the former. These were mostly around # `MockOpen`'s constructor requiring a "path" argument and `FileLikeMock`'s `__exit__` calling # `close` on the underlying file-like object. # # Copyright © 2001-2020 Python Software Foundation; All Rights Reserved. import unittest from mock_open import MockOpen as mock_open try: # pylint: disable=no-name-in-module from unittest.mock import MagicMock, patch, call except ImportError: from mock import MagicMock, patch, call class TestMockOpen(unittest.TestCase): def test_mock_open(self): mock = mock_open() with patch('%s.open' % __name__, mock, create=True) as patched: self.assertIs(patched, mock) open('foo') mock.assert_called_once_with('foo') 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() expected_calls = [call('foo'), call().__enter__(), call().read(), call().__exit__(None, None, None), call().close()] self.assertEqual(mock.mock_calls, expected_calls) self.assertIs(f, handle) 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().close(), call('bar'), call().__enter__(), call().read(), call().__exit__(None, None, None), call().close()] self.assertEqual(mock.mock_calls, expected_calls) def test_explicit_mock(self): mock = MagicMock() mock_open(mock) with patch('%s.open' % __name__, mock, create=True) as patched: self.assertIs(patched, mock) open('foo') mock.assert_called_once_with('foo') def test_read_data(self): mock = mock_open(read_data='foo') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.read() self.assertEqual(result, 'foo') def test_readline_data(self): # Check that readline will return all the lines from the fake file # And that once fully consumed, readline will return an empty string. mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() line2 = h.readline() line3 = h.readline() self.assertEqual(line1, 'foo\n') self.assertEqual(line2, 'bar\n') self.assertEqual(line3, 'baz\n') self.assertEqual(h.readline(), '') # Check that we properly emulate a file that doesn't end in a newline mock = mock_open(read_data='foo') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readline() self.assertEqual(result, 'foo') self.assertEqual(h.readline(), '') def test_dunder_iter_data(self): # Check that dunder_iter will return all the lines from the fake file. mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') lines = [l for l in h] self.assertEqual(lines[0], 'foo\n') self.assertEqual(lines[1], 'bar\n') self.assertEqual(lines[2], 'baz\n') self.assertEqual(h.readline(), '') with self.assertRaises(StopIteration): next(h) def test_next_data(self): # Check that next will correctly return the next available # line and plays well with the dunder_iter part. mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = next(h) line2 = next(h) lines = [l for l in h] self.assertEqual(line1, 'foo\n') self.assertEqual(line2, 'bar\n') self.assertEqual(lines[0], 'baz\n') self.assertEqual(h.readline(), '') def test_readlines_data(self): # Test that emulating a file that ends in a newline character works mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readlines() self.assertEqual(result, ['foo\n', 'bar\n', 'baz\n']) # Test that files without a final newline will also be correctly # emulated mock = mock_open(read_data='foo\nbar\nbaz') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readlines() self.assertEqual(result, ['foo\n', 'bar\n', 'baz']) def test_read_bytes(self): mock = mock_open(read_data=b'\xc6') with patch('%s.open' % __name__, mock, create=True): with open('abc', 'rb') as f: result = f.read() self.assertEqual(result, b'\xc6') def test_readline_bytes(self): m = mock_open(read_data=b'abc\ndef\nghi\n') with patch('%s.open' % __name__, m, create=True): with open('abc', 'rb') as f: line1 = f.readline() line2 = f.readline() line3 = f.readline() self.assertEqual(line1, b'abc\n') self.assertEqual(line2, b'def\n') self.assertEqual(line3, b'ghi\n') def test_readlines_bytes(self): m = mock_open(read_data=b'abc\ndef\nghi\n') with patch('%s.open' % __name__, m, create=True): with open('abc', 'rb') as f: result = f.readlines() self.assertEqual(result, [b'abc\n', b'def\n', b'ghi\n']) def test_mock_open_read_with_argument(self): # At one point calling read with an argument was broken # for mocks returned by mock_open some_data = 'foo\nbar\nbaz' mock = mock_open(read_data=some_data) self.assertEqual(mock('/some/file').read(10), some_data[:10]) self.assertEqual(mock('/some/file').read(10), some_data[:10]) f = mock('/some/file') self.assertEqual(f.read(10), some_data[:10]) self.assertEqual(f.read(10), some_data[10:]) def test_interleaved_reads(self): # Test that calling read, readline, and readlines pulls data # sequentially from the data we preload with mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() rest = h.readlines() self.assertEqual(line1, 'foo\n') self.assertEqual(rest, ['bar\n', 'baz\n']) mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() rest = h.read() self.assertEqual(line1, 'foo\n') self.assertEqual(rest, 'bar\nbaz\n') def test_overriding_return_values(self): mock = mock_open(read_data='foo') handle = mock('/some/file') handle.read.return_value = 'bar' handle.readline.return_value = 'bar' handle.readlines.return_value = ['bar'] self.assertEqual(handle.read(), 'bar') self.assertEqual(handle.readline(), 'bar') self.assertEqual(handle.readlines(), ['bar']) # call repeatedly to check that a StopIteration is not propagated self.assertEqual(handle.readline(), 'bar') self.assertEqual(handle.readline(), 'bar') nivbend-mock-open-deb644f/src/mock_open/test/test_mocks.py000066400000000000000000000573441364562321600240170ustar00rootroot00000000000000"""Test cases for the mocks module.""" import sys import unittest from functools import wraps from mock_open.mocks import MockOpen, FileLikeMock try: # pylint: disable=no-name-in-module from unittest.mock import patch, call, NonCallableMock, DEFAULT except ImportError: from mock import patch, call, NonCallableMock, DEFAULT if sys.version_info < (3, 0): OPEN = '__builtin__.open' else: OPEN = 'builtins.open' @patch(OPEN, new_callable=MockOpen) class TestOpenSingleFiles(unittest.TestCase): """Test the MockOpen and FileLikeMock classes for single file usage.""" def test_read(self, mock_open): """Check effects of reading from an empty file.""" handle = open('/path/to/file', 'r') self.assertFalse(handle.closed) self.assertEqual('/path/to/file', handle.name) self.assertEqual('r', handle.mode) self.assertEqual(0, handle.tell()) text = handle.read() self.assertEqual(0, handle.tell()) self.assertEqual('', text) handle.close() self.assertTrue(handle.closed) self.assertEqual(handle, mock_open.return_value) mock_open.assert_called_once_with('/path/to/file', 'r') handle.read.assert_called_once_with() handle.close.assert_called_once_with() def test_write(self, mock_open): """Check effects of writing to a file.""" handle = open('/path/to/file', 'w') self.assertFalse(handle.closed) self.assertEqual('/path/to/file', handle.name) self.assertEqual('w', handle.mode) self.assertEqual(0, handle.tell()) handle.write("some text\n") self.assertEqual(len("some text\n"), handle.tell()) handle.write('More text!') self.assertEqual( len('some text\n') + len('More text!'), handle.tell()) handle.close() self.assertTrue(handle.closed) self.assertEqual(handle, mock_open.return_value) mock_open.assert_called_once_with('/path/to/file', 'w') self.assertEqual( [call('some text\n'), call('More text!'), ], handle.write.mock_calls) self.assertEqual('some text\nMore text!', handle.read_data) handle.close.assert_called_once_with() def test_context_manager(self, mock_open): """Check calls made when `open` is used as a context manager.""" with open('/path/to/file', 'r') as handle: self.assertFalse(handle.closed) self.assertEqual('/path/to/file', handle.name) self.assertEqual('r', handle.mode) self.assertEqual(0, handle.tell()) mock_open.assert_has_calls([ call('/path/to/file', 'r'), call().__enter__(), call().tell(), call().__exit__(None, None, None), call().close(), ]) def test_read_as_context_manager(self, mock_open): """Check effects of reading from an empty file using a context manager. """ with open('/path/to/file', 'r') as handle: text = handle.read() self.assertEqual(0, handle.tell()) self.assertEqual('', text) self.assertTrue(handle.closed) self.assertEqual(handle, mock_open.return_value) mock_open.assert_called_once_with('/path/to/file', 'r') handle.read.assert_called_once_with() handle.close.assert_called_once_with() def test_write_as_context_manager(self, mock_open): """Check effects of writing to a file using a context manager.""" with open('/path/to/file', 'w') as handle: handle.write("some text\n") self.assertEqual(len("some text\n"), handle.tell()) handle.write('More text!') self.assertEqual( len('some text\n') + len('More text!'), handle.tell()) self.assertTrue(handle.closed) self.assertEqual(handle, mock_open.return_value) mock_open.assert_called_once_with('/path/to/file', 'w') self.assertEqual( [call('some text\n'), call('More text!'), ], handle.write.mock_calls) self.assertEqual('some text\nMore text!', handle.read_data) handle.close.assert_called_once_with() def test_seek(self, _): """Check calling seek().""" with open('/path/to/file', 'w+') as handle: handle.write('There\'s no place like home') handle.seek(len('There\'s ')) self.assertEqual('no place like home', handle.read()) def test_set_contents(self, mock_open): """Check setting file's contents before reading from it.""" contents = [ 'This is the first line', 'This is the second', 'This is the third line', ] # We even allow adding contents to the file incrementally. mock_open.return_value.read_data = '\n'.join(contents[:-1]) mock_open.return_value.read_data += '\n' + contents[-1] with open('/path/to/file', 'r') as handle: data = handle.read() handle.read.assert_called_once_with() self.assertEqual('\n'.join(contents), data) def test_read_size(self, mock_open): """Check reading a certain amount of bytes from the file.""" mock_open.return_value.read_data = '0123456789' with open('/path/to/file', 'r') as handle: self.assertEqual('0123', handle.read(4)) self.assertEqual('4567', handle.read(4)) self.assertEqual('89', handle.read()) def test_different_read_calls(self, mock_open): """Check that read/readline/readlines all work in sync.""" contents = [ 'Now that she\'s back in the atmosphere', 'With drops of Jupiter in her hair, hey, hey, hey', 'She acts like summer and walks like rain', 'Reminds me that there\'s a time to change, hey, hey, hey', 'Since the return from her stay on the moon', 'She listens like spring and she talks like June, hey, hey, hey', ] mock_open.return_value.read_data = '\n'.join(contents) with open('/path/to/file', 'r') as handle: first_line = handle.read(len(contents[0]) + 1) second_line = handle.readline() third_line = handle.read(len(contents[2]) + 1) rest = handle.readlines() self.assertEqual(contents[0] + '\n', first_line) self.assertEqual(contents[1] + '\n', second_line) self.assertEqual(contents[2] + '\n', third_line) self.assertEqual('\n'.join(contents[3:]), ''.join(rest)) def test_different_write_calls(self, _): """Check multiple calls to write and writelines.""" contents = [ 'They paved paradise', 'And put up a parking lot', 'With a pink hotel, a boutique', 'And a swinging hot SPOT', 'Don\'t it always seem to go', 'That you don\'t know what you\'ve got', '\'Til it\'s gone', 'They paved paradise', 'And put up a parking lot', ] with open('/path/to/file', 'w') as handle: handle.write(contents[0] + '\n') handle.write(contents[1] + '\n') handle.writelines(line + '\n' for line in contents[2:4]) handle.write(contents[4] + '\n') handle.writelines(line + '\n' for line in contents[5:]) self.assertEqual(contents, handle.read_data.splitlines()) def test_iteration(self, mock_open): """Test iterating over the file handle.""" contents = [ "So bye, bye, Miss American Pie\n", "Drove my Chevy to the levee but the levee was dry\n", "And them good ole boys were drinking whiskey 'n rye\n", "Singin' this'll be the day that I die\n", 'This\'ll be the day that I die', ] mock_open.return_value.read_data = ''.join(contents) with open('/path/to/file') as handle: for (i, line) in enumerate(handle): self.assertEqual(contents[i], line) # Same thing but using `next`. with open('/path/to/file') as handle: for line in contents: self.assertEqual(line, next(handle)) with self.assertRaises(StopIteration): next(handle) def test_getitem_after_call(self, mock_open): """Retrieving a handle after the call to open() should give us the same object. """ with open('/path/to/file', 'r') as handle: pass self.assertEqual(handle, mock_open['/path/to/file']) def test_setting_custom_mock(self, mock_open): """Check 'manually' setting a mock for a file.""" custom_mock = NonCallableMock() mock_open['/path/to/file'] = custom_mock # Make sure other files aren't affected. self.assertIsInstance(open('/path/to/other_file', 'r'), FileLikeMock) # Check with a regular call. self.assertEqual(custom_mock, open('/path/to/file', 'r')) # Check as a context manager. custom_mock.read.side_effect = IOError() custom_mock.write.side_effect = IOError() with open('/path/to/file') as handle: self.assertIs(custom_mock, handle) self.assertRaises(IOError, handle.read) self.assertRaises(IOError, handle.write, 'error') @patch(OPEN, new_callable=MockOpen) class TestMultipleCalls(unittest.TestCase): """Test multiple calls to open().""" def test_read_then_write(self, _): """Accessing the same file should handle the same object. Reading from a file after writing to it should give us the same contents. """ with open('/path/to/file', 'w') as first_handle: first_handle.write('Ground control to Major Tom') self.assertEqual('w', first_handle.mode) with open('/path/to/file', 'r') as second_handle: contents = second_handle.read() self.assertEqual('r', second_handle.mode) self.assertEqual(first_handle, second_handle) self.assertEqual('Ground control to Major Tom', contents) def test_access_different_files(self, mock_open): """Check access to different files with multiple calls to open().""" first_handle = mock_open['/path/to/first_file'] second_handle = mock_open['/path/to/second_file'] # Paths should be set when created, if possible. # Note this isn't the case when not specifically instantiating a file # mock (eg., by using `return_value` instead). self.assertEqual('/path/to/first_file', first_handle.name) self.assertEqual('/path/to/second_file', second_handle.name) first_handle.read_data = 'This is the FIRST file' second_handle.read_data = 'This is the SECOND file' with open('/path/to/first_file', 'r') as handle: self.assertEqual('/path/to/first_file', handle.name) self.assertEqual('This is the FIRST file', handle.read()) with open('/path/to/second_file', 'r') as handle: self.assertEqual('/path/to/second_file', handle.name) self.assertEqual('This is the SECOND file', handle.read()) # return_value is set to the last handle returned. self.assertEqual(second_handle, mock_open.return_value) self.assertEqual('r', first_handle.mode) self.assertEqual('r', second_handle.mode) first_handle.read.assert_called_once_with() second_handle.read.assert_called_once_with() def test_return_value(self, mock_open): """Check that `return_value` always returns the last file mock.""" with open('/path/to/first_file', 'r'): pass with open('/path/to/second_file', 'r') as handle: self.assertEqual(handle, mock_open.return_value) def test_multiple_opens(self, mock_open): """Verify position when opening file multiple times.""" mock_open.return_value.read_data = 'SOME DATA' with open('/some/file') as first_handle: self.assertEqual(0, first_handle.tell()) self.assertEqual('SOME', first_handle.read(4)) self.assertEqual(4, first_handle.tell()) # Open file a second time. with open('/some/file') as second_handle: self.assertEqual(0, second_handle.tell()) # Open file a third time to append to it. with open('/some/file', 'a') as third_handle: self.assertEqual(9, third_handle.tell()) # Same thing, but not as a context manager. # TODO: In this case we differ from `open` implementation since all of these handles # are actually the same `FileLikeMock` instance. A better mock would provide multiple # instances. first_handle = open('/some/file') self.assertEqual(0, first_handle.tell()) self.assertEqual('SOME', first_handle.read(4)) self.assertEqual(4, first_handle.tell()) second_handle = open('/some/file') self.assertEqual(0, second_handle.tell()) self.assertNotEqual(4, first_handle.tell()) # FIXME? third_handle = open('/some/file', 'a') self.assertEqual(9, third_handle.tell()) self.assertNotEqual(4, first_handle.tell()) # FIXME? self.assertNotEqual(0, second_handle.tell()) # FIXME? @patch(OPEN, new_callable=MockOpen) class TestSideEffects(unittest.TestCase): """Test setting the side_effect attribute in various situations.""" def test_error_on_open(self, mock_open): """Simulate error opening a file.""" mock_open.side_effect = IOError() self.assertRaises(IOError, open, '/not/there', 'r') def test_error_on_any_open(self, mock_open): """Simulate errors opening any file.""" mock_open.side_effect = IOError() self.assertRaises(IOError, open, '/not/there_1', 'r') self.assertRaises(IOError, open, '/not/there_2', 'r') self.assertRaises(IOError, open, '/not/there_3', 'r') def test_error_on_all_but_one(self, mock_open): """Setting a global error but allowing specific file/s.""" mock_open.side_effect = IOError() mock_open['/is/there'].side_effect = None self.assertRaises(IOError, open, '/not/there_1', 'r') self.assertRaises(IOError, open, '/not/there_2', 'r') with open('/is/there', 'r'): pass def test_error_list(self, mock_open): """Setting a global side effect iterator.""" mock_open.side_effect = [ValueError(), RuntimeError(), DEFAULT, ] self.assertRaises(ValueError, open, '/not/there_1', 'r') self.assertRaises(RuntimeError, open, '/not/there_2', 'r') with open('/is/there', 'r'): pass def test_error_on_read(self, mock_open): """Simulate error when reading from file.""" mock_open.return_value.read.side_effect = IOError() with open('/path/to/file', 'w') as handle: with self.assertRaises(IOError): handle.read() def test_error_on_write(self, mock_open): """Simulate error when writing to file.""" mock_open.return_value.write.side_effect = IOError() with open('/path/to/file', 'r') as handle: with self.assertRaises(IOError): handle.write('text') def test_error_by_name(self, mock_open): """Raise an exception for a specific path.""" mock_open['/path/to/error_file'].side_effect = IOError() # Trying to open a different file should be OK. with open('/path/to/allowed_file', 'r'): pass # But opening the bad file should raise an exception. self.assertRaises(IOError, open, '/path/to/error_file', 'r') # Reset open's side effect and check read/write side effects. mock_open['/path/to/error_file'].side_effect = None mock_open['/path/to/error_file'].read.side_effect = IOError() mock_open['/path/to/error_file'].write.side_effect = IOError() with open('/path/to/error_file', 'r') as handle: self.assertRaises(IOError, handle.read) self.assertRaises(IOError, handle.write, 'Bad write') def test_read_return_value(self, mock_open): """Set the return value from read().""" mock_open.return_value.read_data = 'Some text' mock_open.return_value.read.return_value = 'New text' with open('/path/to/file', 'w') as handle: contents = handle.read() self.assertEqual('New text', contents) def test_read_side_effect(self, mock_open): """Add a side effect to read(). Setting a side_effect can't change the return value. """ def set_sentinal(): # pylint: disable=missing-docstring sentinal[0] = True return DEFAULT # If we define contents as a 'simple' variable (just None, for example) # the assignment inside fake_write() will assign to a local variable # instead of the 'outer' contents variable. sentinal = [False, ] mock_open.return_value.read_data = 'Some text' mock_open.return_value.read.side_effect = set_sentinal with open('/path/to/file', 'w') as handle: contents = handle.read() self.assertEqual('Some text', contents) self.assertTrue(sentinal[0]) def test_write_side_effect(self, mock_open): """Add a side effect to write().""" def set_sentinal(data): # pylint: disable=missing-docstring sentinal[0] = True return DEFAULT # Avoid uninitialized assignment (see test_read_side_effect()). sentinal = [False, ] mock_open.return_value.write.side_effect = set_sentinal with open('/path/to/file', 'w') as handle: handle.write('Some text') self.assertEqual('Some text', handle.read_data) self.assertTrue(sentinal[0]) def test_multiple_files(self, mock_open): """Test different side effects for different files.""" mock_open['fail_on_open'].side_effect = IOError() mock_open['fail_on_read'].read.side_effect = IOError() mock_open['fail_on_write'].write.side_effect = IOError() with open('success', 'w') as handle: handle.write('some text') self.assertRaises(IOError, open, 'fail_on_open', 'rb') with open('fail_on_read', 'r') as handle: self.assertRaises(IOError, handle.read) with open('fail_on_write', 'w') as handle: self.assertRaises(IOError, handle.write, 'not to be written') with open('success', 'r') as handle: self.assertEqual('some text', handle.read()) class TestAPI(unittest.TestCase): """Test conformance to mock library's API.""" def test_read_data(self): """Check passing of `read_data` to the constructor.""" mock_open = MockOpen(read_data='Data from the file') with patch(OPEN, mock_open): with open('/path/to/file', 'r') as handle: contents = handle.read() self.assertEqual('Data from the file', contents) def test_reset_mock(self): """Check that reset_mock() works.""" # Reset globally for all file mocks. mock_open = MockOpen(read_data='Global') mock_open['/path/to/file'].read_data = 'File-specific' mock_open.reset_mock() with patch(OPEN, mock_open): with open('/path/to/file', 'r') as handle: self.assertEqual('', handle.read()) # Reset a for a specific file mock. mock_open = MockOpen(read_data='Global') mock_open['/path/to/file'].read_data = 'File-specific' mock_open['/path/to/file'].reset_mock() with patch(OPEN, mock_open): with open('/path/to/file', 'r') as handle: self.assertEqual('', handle.read()) with open('/path/to/other/file', 'r') as handle: self.assertEqual('Global', handle.read()) class TestModes(unittest.TestCase): """Test different modes behavior.""" @patch(OPEN, new_callable=MockOpen) def test_default_mode(self, mock_open): """Default mode is 'r'.""" with open('/path/to/file') as _: pass mock_open.assert_called_once_with('/path/to/file') self.assertEqual('r', mock_open.return_value.mode) @patch(OPEN, new_callable=MockOpen) def test_open_as_text(self, mock_open): """Read/write to file as text (no 'b').""" with open('/path/to/empty_file', 'r') as handle: contents = handle.read() self.assertIsInstance(contents, str) self.assertEqual('', contents) mock_open['/path/to/file'].read_data = 'Contents' with open('/path/to/file', 'r') as handle: contents = handle.read() self.assertIsInstance(contents, str) self.assertEqual('Contents', contents) with open('/path/to/file', 'w') as handle: handle.write('New contents') self.assertEqual('New contents', handle.read_data) @patch(OPEN, new_callable=MockOpen) def test_open_as_binary(self, mock_open): """Read/write to file as binary data (with 'b').""" with open('/path/to/empty_file', 'rb') as handle: contents = handle.read() self.assertIsInstance(contents, bytes) self.assertEqual(b'', contents) mock_open['/path/to/file'].read_data = b'Contents' with open('/path/to/file', 'rb') as handle: contents = handle.read() self.assertIsInstance(contents, bytes) self.assertEqual(b'Contents', contents) with open('/path/to/file', 'wb') as handle: handle.write(b'New contents') self.assertEqual(b'New contents', handle.read_data) @patch(OPEN, new_callable=MockOpen) def test_different_opens(self, _): """Open the same file as text/binary.""" with open('/path/to/file', 'w') as handle: handle.write('Textual content') self.assertIsInstance(handle.read_data, str) with open('/path/to/file', 'rb') as handle: contents = handle.read() self.assertIsInstance(contents, bytes) self.assertEqual(b'Textual content', contents) with open('/path/to/file', 'r') as handle: contents = handle.read() self.assertIsInstance(contents, str) self.assertEqual('Textual content', contents) class TestIssues(unittest.TestCase): """Test cases related to issues on GitHub. See https://github.com/nivbend/mock-open """ def test_issue_1(self): """Setting a side effect on a specific open() shouldn't affect consecutive calls. """ mock_open = MockOpen() mock_open['fail_on_open'].side_effect = IOError() with patch(OPEN, mock_open): with self.assertRaises(IOError): open('fail_on_open', 'rb') with open('success', 'r') as handle: self.assertEqual('', handle.read()) def test_issue_3(self): """Position in file should be set to 0 after the call to `open`.""" mock_open = MockOpen(read_data='some content') with patch(OPEN, mock_open): handle = open('/path/to/file', 'r') self.assertEqual(0, handle.tell()) self.assertEqual('some content', handle.read()) @staticmethod @patch(OPEN, new_callable=MockOpen) def test_issue_4(mock_open): """Assert relative calls after consecutive opens.""" with open('/path/to/file', 'r') as _: pass mock_open.reset_mock() with open('/path/to/file', 'r') as _: pass # The key here is that the last three calls objects are `call()` # instead of `call`. This is fixed by setting _new_name. mock_open.assert_has_calls([ call('/path/to/file', 'r'), call().__enter__(), call().__exit__(None, None, None), call().close(), ]) nivbend-mock-open-deb644f/tox.ini000066400000000000000000000005761364562321600170600ustar00rootroot00000000000000# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py27, py32, py33, py34, py35, py36, py37, py38, py39 [testenv] commands = python -m unittest mock_open.test {posargs}