|
5 | 5 |
|
6 | 6 | Fixture "mock_timing" also interacts with this module for pytest's own tests. |
7 | 7 | """ |
8 | | - |
9 | 8 | from __future__ import annotations |
10 | 9 |
|
| 10 | +import dataclasses |
| 11 | +from datetime import datetime |
11 | 12 | from time import perf_counter |
12 | 13 | from time import sleep |
13 | 14 | from time import time |
| 15 | +from typing import TYPE_CHECKING |
| 16 | + |
| 17 | + |
| 18 | +if TYPE_CHECKING: |
| 19 | + from pytest import MonkeyPatch |
| 20 | + |
| 21 | + |
| 22 | +@dataclasses.dataclass |
| 23 | +class MockTiming: |
| 24 | + """Mocks _pytest.timing with a known object that can be used to control timing in tests |
| 25 | + deterministically. |
| 26 | +
|
| 27 | + pytest itself should always use functions from `_pytest.timing` instead of `time` directly. |
| 28 | +
|
| 29 | + This then allows us more control over time during testing, if testing code also |
| 30 | + uses `_pytest.timing` functions. |
| 31 | +
|
| 32 | + Time is static, and only advances through `sleep` calls, thus tests might sleep over large |
| 33 | + numbers and obtain accurate time() calls at the end, making tests reliable and instant.""" |
| 34 | + |
| 35 | + _current_time: float = datetime(2020, 5, 22, 14, 20, 50).timestamp() # noqa: RUF009 |
| 36 | + |
| 37 | + def sleep(self, seconds: float) -> None: |
| 38 | + self._current_time += seconds |
| 39 | + |
| 40 | + def time(self) -> float: |
| 41 | + return self._current_time |
| 42 | + |
| 43 | + def patch(self, monkeypatch: MonkeyPatch) -> None: |
| 44 | + from _pytest import timing |
| 45 | + monkeypatch.setattr(timing, "sleep", self.sleep) |
| 46 | + monkeypatch.setattr(timing, "time", self.time) |
| 47 | + monkeypatch.setattr(timing, "perf_counter", self.time) |
14 | 48 |
|
15 | 49 |
|
16 | 50 | __all__ = ["perf_counter", "sleep", "time"] |
0 commit comments