0
# Test Utilities and Environment Control
1
2
Utilities for controlling test environments including monkeypatching, output capture, temporary paths, and specialized tools for testing pytest plugins. These utilities provide comprehensive control over the test execution environment.
3
4
## Capabilities
5
6
### Environment Modification
7
8
MonkeyPatch provides safe, temporary modifications to objects, environment variables, and system paths.
9
10
```python { .api }
11
class MonkeyPatch:
12
"""Helper for temporarily modifying objects, environment, and sys.path."""
13
14
def setattr(self, target, name, value=<notset>, raising: bool = True) -> None:
15
"""Set attribute on target object."""
16
17
def setitem(self, dic, name, value) -> None:
18
"""Set dictionary item."""
19
20
def setenv(self, name: str, value: str, prepend: str | None = None) -> None:
21
"""Set environment variable."""
22
23
def syspath_prepend(self, path) -> None:
24
"""Prepend to sys.path."""
25
26
def chdir(self, path) -> None:
27
"""Change current working directory."""
28
29
def undo(self) -> None:
30
"""Undo all changes."""
31
```
32
33
**Usage Example:**
34
35
```python
36
def test_monkeypatch(monkeypatch):
37
# Modify object attribute
38
monkeypatch.setattr("os.getcwd", lambda: "/fake/path")
39
40
# Set environment variable
41
monkeypatch.setenv("TEST_MODE", "true")
42
43
# Modify dictionary
44
import sys
45
monkeypatch.setitem(sys.modules, "fake_module", FakeModule())
46
47
# Change directory
48
monkeypatch.chdir("/tmp")
49
50
# All changes are automatically undone after test
51
```
52
53
### Output Capture
54
55
Capture stdout, stderr, and file descriptor output during test execution.
56
57
```python { .api }
58
class CaptureFixture:
59
"""Captures stdout/stderr output during tests."""
60
61
def readouterr(self) -> CaptureResult:
62
"""Read and return captured output."""
63
64
def disabled(self):
65
"""Context manager to temporarily disable capturing."""
66
67
class CaptureResult:
68
"""Result of captured output."""
69
out: str # Captured stdout
70
err: str # Captured stderr
71
```
72
73
**Usage Example:**
74
75
```python
76
def test_output_capture(capsys):
77
print("Hello stdout")
78
print("Hello stderr", file=sys.stderr)
79
80
captured = capsys.readouterr()
81
assert captured.out == "Hello stdout\\n"
82
assert captured.err == "Hello stderr\\n"
83
84
# Temporarily disable capture
85
with capsys.disabled():
86
print("This will be printed normally")
87
88
# Different capture fixtures available:
89
def test_capture_variants(capsys, capsysbinary, capfd, capfdbinary):
90
# capsys: capture sys.stdout/stderr (text)
91
# capsysbinary: capture sys.stdout/stderr (binary)
92
# capfd: capture file descriptors 1/2 (text)
93
# capfdbinary: capture file descriptors 1/2 (binary)
94
pass
95
```
96
97
### Log Capture
98
99
Capture and inspect log messages during test execution.
100
101
```python { .api }
102
class LogCaptureFixture:
103
"""Provides access and control of log capturing."""
104
105
# Attributes
106
handler: LogCaptureHandler # Log handler
107
records: list[logging.LogRecord] # Captured log records
108
109
def get_records(self, when: str) -> list[logging.LogRecord]:
110
"""Get log records for specific test phase."""
111
112
def clear(self) -> None:
113
"""Clear captured records."""
114
115
def set_level(self, level: int | str, logger: str | None = None) -> None:
116
"""Set logging level."""
117
```
118
119
**Usage Example:**
120
121
```python
122
import logging
123
124
def test_logging(caplog):
125
with caplog.at_level(logging.INFO):
126
logging.info("This is an info message")
127
logging.warning("This is a warning")
128
129
assert len(caplog.records) == 2
130
assert caplog.records[0].levelname == "INFO"
131
assert "info message" in caplog.records[0].message
132
133
# Clear records
134
caplog.clear()
135
assert len(caplog.records) == 0
136
137
# Check specific logger
138
logger = logging.getLogger("myapp")
139
logger.error("Application error")
140
141
assert any("Application error" in record.message
142
for record in caplog.records)
143
```
144
145
### Temporary Paths
146
147
Factory for creating temporary directories and files.
148
149
```python { .api }
150
class TempPathFactory:
151
"""Factory for creating temporary directories."""
152
153
def mktemp(self, basename: str, numbered: bool = True) -> Path:
154
"""Create temporary directory."""
155
156
def getbasetemp(self) -> Path:
157
"""Get base temporary directory."""
158
159
# Built-in fixtures
160
def test_temp_paths(tmp_path, tmp_path_factory):
161
# tmp_path: pathlib.Path to temporary directory (function scope)
162
# tmp_path_factory: TempPathFactory instance
163
164
# Create file in temporary directory
165
file_path = tmp_path / "test_file.txt"
166
file_path.write_text("test content")
167
assert file_path.read_text() == "test content"
168
169
# Create additional temp directory
170
another_temp = tmp_path_factory.mktemp("custom_dir")
171
assert another_temp.exists()
172
```
173
174
### Plugin Testing Framework
175
176
Comprehensive tools for testing pytest plugins and functionality.
177
178
```python { .api }
179
class Pytester:
180
"""Facilities for testing pytest plugins and functionality."""
181
182
def makepyfile(self, **kwargs) -> Path:
183
"""Create Python file with content."""
184
185
def makeconftest(self, source: str) -> Path:
186
"""Create conftest.py file."""
187
188
def makefile(self, ext: str, **kwargs) -> Path:
189
"""Create file with given extension."""
190
191
def mkdir(self, name: str) -> Path:
192
"""Create directory."""
193
194
def runpytest(self, *args, **kwargs) -> RunResult:
195
"""Run pytest in subprocess."""
196
197
def runpytest_subprocess(self, *args, **kwargs) -> RunResult:
198
"""Run pytest in subprocess with isolation."""
199
200
def runpytest_inprocess(self, *args, **kwargs) -> RunResult:
201
"""Run pytest in same process."""
202
203
class RunResult:
204
"""Result of running pytest in subprocess."""
205
206
# Attributes
207
ret: int # Return code
208
outlines: list[str] # stdout lines
209
errlines: list[str] # stderr lines
210
stdout: str # Full stdout
211
stderr: str # Full stderr
212
duration: float # Execution duration
213
214
def parseoutcomes(self) -> dict[str, int]:
215
"""Parse test outcomes from output."""
216
217
def assert_outcomes(self, **expected) -> None:
218
\"\"\"Assert expected test outcomes.\"\"\"\n\nclass HookRecorder:\n \"\"\"Records hook calls for testing.\"\"\"\n \n def getcalls(self, names: str | list[str]) -> list[RecordedHookCall]:\n \"\"\"Get recorded calls for hook names.\"\"\"\n \n def assert_contains(self, entries) -> None:\n \"\"\"Assert recorder contains specific entries.\"\"\"\n \n def pop(self, name: str) -> RecordedHookCall:\n \"\"\"Remove and return last call for hook name.\"\"\"\n\nclass RecordedHookCall:\n \"\"\"Represents a recorded hook call.\"\"\"\n pass\n\nclass LineMatcher:\n \"\"\"Flexible matching of text output lines.\"\"\"\n \n def fnmatch_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using fnmatch patterns.\"\"\"\n \n def re_match_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using regex patterns.\"\"\"\n \n def no_fnmatch_line(self, pat: str) -> None:\n \"\"\"Assert no line matches fnmatch pattern.\"\"\"\n```\n\n**Usage Example:**\n\n```python\ndef test_my_plugin(pytester):\n # Create test files\n pytester.makepyfile(\"\"\"\n def test_example():\n assert True\n \"\"\")\n \n pytester.makeconftest(\"\"\"\n import pytest\n \n @pytest.fixture\n def my_fixture():\n return \"test_value\"\n \"\"\")\n \n # Run pytest\n result = pytester.runpytest(\"-v\")\n \n # Check results\n result.assert_outcomes(passed=1)\n assert result.ret == 0\n \n # Check output\n result.stdout.fnmatch_lines([\n \"*test_example PASSED*\"\n ])\n\ndef test_hook_recording(pytester):\n pytester.makepyfile(\"\"\"\n def test_foo(): pass\n \"\"\")\n \n # Record hooks\n rec = pytester.runpytest(\"--collect-only\")\n \n # Check hook calls\n calls = rec.getcalls(\"pytest_collection_modifyitems\")\n assert len(calls) == 1\n```\n\n### Legacy Path Support (Deprecated)\n\nLegacy support for py.path and testdir (deprecated in favor of pathlib.Path).\n\n```python { .api }\nclass TempdirFactory:\n \"\"\"Legacy factory for temporary directories (deprecated).\"\"\"\n pass\n\nclass Testdir:\n \"\"\"Legacy test directory helper (deprecated).\"\"\"\n pass\n```\n\n## Built-in Utility Fixtures\n\nSummary of built-in fixtures for test utilities:\n\n```python\ndef test_with_all_utilities(\n monkeypatch, # MonkeyPatch instance\n capsys, # Capture sys.stdout/stderr (text)\n capsysbinary, # Capture sys.stdout/stderr (binary)\n capfd, # Capture file descriptors (text)\n capfdbinary, # Capture file descriptors (binary) \n caplog, # LogCaptureFixture instance\n tmp_path, # pathlib.Path to temp directory\n tmp_path_factory, # TempPathFactory instance\n pytester, # Pytester instance (for plugin testing)\n recwarn, # WarningsRecorder instance\n # Legacy (deprecated)\n tmpdir, # py.path.local temp directory\n tmpdir_factory, # TempdirFactory instance\n testdir # Testdir instance\n):\n pass\n```