Mock Open
The `mock-open` library (current version 1.4.0) provides an enhanced mock object for testing file I/O operations in Python. It extends `unittest.mock.mock_open` to offer more realistic behavior for file-like objects, including robust handling of `with` statements, `read`, `write`, `seek`, and binary modes. It is actively maintained with releases addressing compatibility and feature enhancements.
Common errors
-
AttributeError: 'MockOpen' object has no attribute 'read'
cause Attempting to call file-like methods (like `read()`, `write()`, `seek()`) directly on the `MockOpen` instance instead of on the mock file handle returned by `open()` or accessed via `mo.mock_handle`.fixAfter patching `builtins.open` with a `MockOpen` instance (`mo`), you must interact with the file object returned by `open` (e.g., `with open(...) as f: f.read()`) or directly access `mo.mock_handle` for assertions (e.g., `mo.mock_handle.read.assert_called_once()`). -
AssertionError: Expected 'some_data', but received '' (empty string) or 'None'
cause Forgetting to provide initial data to `MockOpen` via the `read_data` argument when mocking a file read operation.fixWhen initializing `MockOpen`, pass the expected content as a string (or bytes for binary mode) to the `read_data` parameter: `mo = MockOpen(read_data='Expected file content')`. -
TypeError: a bytes-like object is required, not 'str'
cause Mismatch between the file mode (e.g., 'rb' for binary, 'r' for text) and the type of data provided (`read_data`) or written to the mock file. For binary modes, `read_data` and `write` arguments must be `bytes`; for text modes, they must be `str`.fixEnsure `read_data` and any data passed to `f.write()` match the expected type for the file mode. For binary modes (`'rb'`, `'wb'`), use `b"..."` for bytes. For text modes (`'r'`, `'w'`), use `"..."` for strings.
Warnings
- breaking Prior to v1.3.0, `mock-open` often required contrived `side_effect` workarounds for file operations (`read`, `write`, `seek`). V1.3.0 changed this to use direct mock wrapping, making `return_value` and direct attribute access (`.mock_handle.read`) the correct way to interact. Code relying on `side_effect` for content might break.
- gotcha In `mock-open` v1.4.0+, consecutive calls to a patched `open` function will reset the file-like mock's internal position (like `seek(0)`). In previous versions, the position might have persisted across different `with open(...)` blocks using the same mock, leading to unexpected behavior in subsequent reads.
- gotcha When mocking binary file operations (`'rb'`, `'wb'`, etc.) with `mock-open` (v1.3.0+), the underlying content storage uses `io.BytesIO`. Ensure that `read_data` provided for binary modes is a `bytes` object, not a `str`, and that assertions check for `bytes` content.
Install
-
pip install mock-open
Imports
- MockOpen
from mock_open import MockOpen
- patch
from unittest.mock import patch
Quickstart
from unittest.mock import patch
from mock_open import MockOpen
# Scenario 1: Mocking file reading
mock_data_to_read = "Line 1\nLine 2\nLine 3"
mo_read = MockOpen(read_data=mock_data_to_read)
with patch("builtins.open", mo_read):
with open("my_input.txt", "r") as f:
content = f.read()
lines = f.readlines()
assert content == mock_data_to_read
assert lines == ["Line 1\n", "Line 2\n", "Line 3"]
mo_read.assert_called_with("my_input.txt", "r")
# Scenario 2: Mocking file writing
mo_write = MockOpen()
with patch("builtins.open", mo_write):
with open("my_output.txt", "w") as f:
f.write("Hello, world!")
f.write("Python is great.")
# Access the mock handle to verify writes
mo_write.mock_handle.write.assert_any_call("Hello, world!")
mo_write.mock_handle.write.assert_any_call("Python is great.")
mo_write.assert_called_with("my_output.txt", "w")
# You can get the content written to the mock
written_content = "".join(call.args[0] for call in mo_write.mock_handle.write.call_args_list)
assert written_content == "Hello, world!Python is great."
print("Mocking successful!")