test_monkeypatch.py
Overview
This file contains a comprehensive suite of tests for the `MonkeyPatch` utility class from the `_pytest.monkeypatch` module. The `MonkeyPatch` class is a powerful tool used primarily in testing environments to safely and temporarily modify or replace attributes, dictionary items, environment variables, and system states such as the current working directory or the Python import path (`sys.path`).
The tests in this file validate `MonkeyPatch`'s core functionalities, including setting and deleting attributes and items, patching environment variables, manipulating the Python import system, and ensuring proper undoing of patches to revert system state after tests run. It also covers edge cases, error handling, and integration behavior with other parts of the `pytest` ecosystem.
Detailed Components
Imports and Fixtures
Imports: The file imports standard library modules (
os,sys,re,pathlib.Path,textwrap), pytest utilities (pytest,_pytest.monkeypatch.MonkeyPatch,_pytest.pytester.Pytester), andsetuptools(for conditional tests).Fixture:
mp@pytest.fixture def mp() -> Generator[MonkeyPatch]: cwd = os.getcwd() sys_path = list(sys.path) yield MonkeyPatch() sys.path[:] = sys_path os.chdir(cwd)Provides a
MonkeyPatchinstance to tests, ensuring after the test completes thatsys.pathand the current working directory (cwd) are restored to their original states to avoid side effects.
Test Functions and Classes
1. test_setattr()
Tests the `setattr` method of `MonkeyPatch` for:
Setting and undoing attribute changes on classes.
Behavior when attempting to set attributes that don't exist, with and without raising errors.
Handling multiple attribute sets and undo operations.
Ensuring errors are raised on incorrect usage.
**Usage example:**
class A:
x = 1
monkeypatch = MonkeyPatch()
monkeypatch.setattr(A, "x", 2)
assert A.x == 2
monkeypatch.undo()
assert A.x == 1
2. TestSetattrWithImportPath
A class grouping tests for `MonkeyPatch.setattr` when using string import paths to specify the target attribute:
Patching attributes via import path strings (e.g.,
"os.path.abspath").Handling Unicode strings.
Behavior when the target module or attribute is unknown.
The
raisingflag effect on error handling.Deleting patched attributes with
delattr.
3. test_delattr()
Tests the `delattr` method:
Deleting attributes from classes.
Undoing deletions.
Handling deletion of non-existing attributes with
raisingflags.
4. test_setitem() and test_delitem()
Test the patching of dictionary items via `setitem` and `delitem`:
Setting, updating, and deleting dictionary keys.
Undo behavior restoring original dictionary state.
Handling keys deleted or missing during patch lifecycle.
5. Environment Variable Tests: test_setenv(), test_delenv(), TestEnvironWarnings, test_setenv_prepend()
Patching environment variables (
os.environ) withsetenvanddelenv.Ensuring environment variable types are strings, warning on implicit conversions.
Prepending values to environment variables (useful for path-like variables).
Proper undoing to restore environment state.
6. test_monkeypatch_plugin
Validates that `monkeypatch` is correctly injected as a pytest fixture and has the expected class.
7. test_syspath_prepend() and related tests
Testing manipulation of
sys.pathby prepending paths.Undoing these changes accurately.
Handling namespace packages and ensuring
pkg_resourcesnamespace fixups.Invalidating import caches when modifying
sys.path.
8. Working Directory Tests: test_chdir_with_path_local(), test_chdir_with_str(), test_chdir_undo(), test_chdir_double_undo()
Changing the current working directory (
os.chdir) safely.Undoing directory changes and handling multiple undos.
9. Issue and Edge Case Tests
test_issue185_time_breaks: Patchingtime.timeto raise exceptions without breaking pytest.test_importerror: Handling import errors gracefully when patching.test_issue156_undo_staticmethod: Undoing patches on static methods.test_undo_class_descriptors_delattr: Undoing deletions on class and static methods.test_issue1338_name_resolving: Patching using string import paths on third-party modules.Context management tests (
test_context,test_context_classmethod) for usingMonkeyPatch.context()as a context manager to scope patches.
Classes
Sample and SampleInherit
Simple classes with a static method `hello()` used in parameterized tests verifying patching and undo of static methods in base and inherited classes.
Important Implementation Details and Algorithms
Undo Stack:
MonkeyPatchmaintains an internal stack recording all changes (attribute sets, deletions, environment changes, etc.) to allow precise restoration uponundo(). This stack supports multiple undo calls gracefully.String Import Path Resolution: When patching using strings (e.g.,
"os.path.abspath"),MonkeyPatchdynamically imports the module and resolves the attribute path, raising meaningful exceptions if the module or attribute does not exist.Environment Variable Handling: Since
os.environexpects string keys and values, the patcher warns when non-string values are provided and implicitly converts them to strings.Namespace Package Handling: When modifying
sys.path,MonkeyPatchensures compatibility with namespace packages by invokingpkg_resources.declare_namespaceand invalidating import caches, ensuring newly added paths are correctly recognized by Python's import system.Context Manager Support:
MonkeyPatch.context()provides a context manager interface for automatic patch undoing, which is safer and more pythonic in tests.
Interaction with Other System Components
pytest Framework: The file is designed to test the
MonkeyPatchutility integrated within thepytesttesting framework, ensuring it behaves correctly as a fixture and utility._pytest.monkeypatch Module: Directly tests the
MonkeyPatchclass from this internal pytest module._pytest.pytester Plugin: Uses
Pytesterfor inline test file creation and pytest invocation to test behavior in real pytest runs.Standard Library Modules: Extensively interacts with
os,sys, andimportlib(indirectly via import machinery) to patch system state and environment.setuptools: Tests for namespace package handling depend on
pkg_resourcesfromsetuptoolsand are conditionally skipped if setuptools version is recent enough to have removedpkg_resources.
Usage Examples
Basic attribute patching and undo
class MyClass:
attr = 10
mp = MonkeyPatch()
mp.setattr(MyClass, "attr", 20)
assert MyClass.attr == 20
mp.undo()
assert MyClass.attr == 10
Patching environment variables with prepend
mp = MonkeyPatch()
mp.setenv("PATH", "/custom/path", prepend=":")
# os.environ['PATH'] now has "/custom/path" prepended
mp.undo()
Using MonkeyPatch as context manager
with MonkeyPatch.context() as mp:
mp.setattr(some_module, "func", lambda: 42)
# patched func active here
# patch undone after block
Mermaid Diagram: Class Diagram for Test Classes
classDiagram
class TestSetattrWithImportPath {
+test_string_expression(monkeypatch)
+test_string_expression_class(monkeypatch)
+test_unicode_string(monkeypatch)
+test_wrong_target(monkeypatch)
+test_unknown_import(monkeypatch)
+test_unknown_attr(monkeypatch)
+test_unknown_attr_non_raising(monkeypatch)
+test_delattr(monkeypatch)
}
class TestEnvironWarnings {
+test_setenv_non_str_warning(monkeypatch)
}
class Sample {
+static hello() bool
}
class SampleInherit {
}
TestSetattrWithImportPath ..|> object
TestEnvironWarnings ..|> object
SampleInherit --|> Sample
Summary
`test_monkeypatch.py` is a critical test suite that ensures the robustness, correctness, and usability of `pytest`'s `MonkeyPatch` utility. It covers a wide range of patching scenarios, including attributes, dictionary items, environment variables, system path manipulation, and working directory changes, while carefully validating undo functionality and error cases. It also ensures compatibility with Python's import system and third-party modules, providing confidence for users relying on `MonkeyPatch` in complex testing environments.