Skip to content

Commit d149ab3

Browse files
assorted code updates and f string migrations (#12017)
* chore: setuponly - migrate to f string * chore: MockTiming - move impl to _pytest.timing * chore: fixture tests: migrate to f-strings * chore: skipping tests: migrate to f-strings * chore: junitxml tests: add more type annotations * chore: junitxml tests: introduce more typesafe helpers * add changelog * test junitxml: split the DomNode type to express the Document/Element difference * fixup: undo accidential name-mangling in the junitxml test helpers * fixup: junitxml logging test - restore validation for expected output nodes * fixup: have fake config .getini use correct types * Update changelog/12017.improvement.rst Co-authored-by: Bruno Oliveira <[email protected]> * use contrib category for changelog * use datetime.fromisoformat instead of strptime * post rebase ruff fixes * add extra coverage for junit test helpers --------- Co-authored-by: Bruno Oliveira <[email protected]>
1 parent 338ef90 commit d149ab3

File tree

7 files changed

+305
-232
lines changed

7 files changed

+305
-232
lines changed

changelog/12017.contrib.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Mixed internal improvements:
2+
3+
* Migrate formatting to f-strings in some tests.
4+
* Use type-safe constructs in JUnitXML tests.
5+
* Moved`` MockTiming`` into ``_pytest.timing``.
6+
7+
-- by :user:`RonnyPfannschmidt`

src/_pytest/setuponly.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,9 @@ def _show_fixture_action(
7373
# Use smaller indentation the higher the scope: Session = 0, Package = 1, etc.
7474
scope_indent = list(reversed(Scope)).index(fixturedef._scope)
7575
tw.write(" " * 2 * scope_indent)
76-
tw.write(
77-
"{step} {scope} {fixture}".format( # noqa: UP032 (Readability)
78-
step=msg.ljust(8), # align the output to TEARDOWN
79-
scope=fixturedef.scope[0].upper(),
80-
fixture=fixturedef.argname,
81-
)
82-
)
76+
77+
scopename = fixturedef.scope[0].upper()
78+
tw.write(f"{msg:<8} {scopename} {fixturedef.argname}")
8379

8480
if msg == "SETUP":
8581
deps = sorted(arg for arg in fixturedef.argnames if arg != "request")

src/_pytest/timing.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,45 @@
88

99
from __future__ import annotations
1010

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

1551

1652
__all__ = ["perf_counter", "sleep", "time"]

testing/conftest.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from __future__ import annotations
33

44
from collections.abc import Generator
5-
import dataclasses
65
import importlib.metadata
76
import re
87
import sys
@@ -233,24 +232,8 @@ def mock_timing(monkeypatch: MonkeyPatch):
233232
Time is static, and only advances through `sleep` calls, thus tests might sleep over large
234233
numbers and obtain accurate time() calls at the end, making tests reliable and instant.
235234
"""
236-
237-
@dataclasses.dataclass
238-
class MockTiming:
239-
_current_time: float = 1590150050.0
240-
241-
def sleep(self, seconds: float) -> None:
242-
self._current_time += seconds
243-
244-
def time(self) -> float:
245-
return self._current_time
246-
247-
def patch(self) -> None:
248-
from _pytest import timing
249-
250-
monkeypatch.setattr(timing, "sleep", self.sleep)
251-
monkeypatch.setattr(timing, "time", self.time)
252-
monkeypatch.setattr(timing, "perf_counter", self.time)
235+
from _pytest.timing import MockTiming
253236

254237
result = MockTiming()
255-
result.patch()
238+
result.patch(monkeypatch)
256239
return result

testing/python/fixtures.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2315,14 +2315,14 @@ def test_ordering_dependencies_torndown_first(
23152315
) -> None:
23162316
"""#226"""
23172317
pytester.makepyfile(
2318-
"""
2318+
f"""
23192319
import pytest
23202320
values = []
2321-
@pytest.fixture(%(param1)s)
2321+
@pytest.fixture({param1})
23222322
def arg1(request):
23232323
request.addfinalizer(lambda: values.append("fin1"))
23242324
values.append("new1")
2325-
@pytest.fixture(%(param2)s)
2325+
@pytest.fixture({param2})
23262326
def arg2(request, arg1):
23272327
request.addfinalizer(lambda: values.append("fin2"))
23282328
values.append("new2")
@@ -2331,8 +2331,7 @@ def test_arg(arg2):
23312331
pass
23322332
def test_check():
23332333
assert values == ["new1", "new2", "fin2", "fin1"]
2334-
""" # noqa: UP031 (python syntax issues)
2335-
% locals()
2334+
"""
23362335
)
23372336
reprec = pytester.inline_run("-s")
23382337
reprec.assertoutcome(passed=2)
@@ -3212,21 +3211,21 @@ def test_finalizer_order_on_parametrization(
32123211
) -> None:
32133212
"""#246"""
32143213
pytester.makepyfile(
3215-
"""
3214+
f"""
32163215
import pytest
32173216
values = []
32183217
3219-
@pytest.fixture(scope=%(scope)r, params=["1"])
3218+
@pytest.fixture(scope={scope!r}, params=["1"])
32203219
def fix1(request):
32213220
return request.param
32223221
3223-
@pytest.fixture(scope=%(scope)r)
3222+
@pytest.fixture(scope={scope!r})
32243223
def fix2(request, base):
32253224
def cleanup_fix2():
32263225
assert not values, "base should not have been finalized"
32273226
request.addfinalizer(cleanup_fix2)
32283227
3229-
@pytest.fixture(scope=%(scope)r)
3228+
@pytest.fixture(scope={scope!r})
32303229
def base(request, fix1):
32313230
def cleanup_base():
32323231
values.append("fin_base")
@@ -3239,8 +3238,7 @@ def test_baz(base, fix2):
32393238
pass
32403239
def test_other():
32413240
pass
3242-
""" # noqa: UP031 (python syntax issues)
3243-
% {"scope": scope}
3241+
"""
32443242
)
32453243
reprec = pytester.inline_run("-lvs")
32463244
reprec.assertoutcome(passed=3)
@@ -3426,42 +3424,40 @@ class TestRequestScopeAccess:
34263424

34273425
def test_setup(self, pytester: Pytester, scope, ok, error) -> None:
34283426
pytester.makepyfile(
3429-
"""
3427+
f"""
34303428
import pytest
3431-
@pytest.fixture(scope=%r, autouse=True)
3429+
@pytest.fixture(scope={scope!r}, autouse=True)
34323430
def myscoped(request):
3433-
for x in %r:
3431+
for x in {ok.split()}:
34343432
assert hasattr(request, x)
3435-
for x in %r:
3433+
for x in {error.split()}:
34363434
pytest.raises(AttributeError, lambda:
34373435
getattr(request, x))
34383436
assert request.session
34393437
assert request.config
34403438
def test_func():
34413439
pass
3442-
""" # noqa: UP031 (python syntax issues)
3443-
% (scope, ok.split(), error.split())
3440+
"""
34443441
)
34453442
reprec = pytester.inline_run("-l")
34463443
reprec.assertoutcome(passed=1)
34473444

34483445
def test_funcarg(self, pytester: Pytester, scope, ok, error) -> None:
34493446
pytester.makepyfile(
3450-
"""
3447+
f"""
34513448
import pytest
3452-
@pytest.fixture(scope=%r)
3449+
@pytest.fixture(scope={scope!r})
34533450
def arg(request):
3454-
for x in %r:
3451+
for x in {ok.split()!r}:
34553452
assert hasattr(request, x)
3456-
for x in %r:
3453+
for x in {error.split()!r}:
34573454
pytest.raises(AttributeError, lambda:
34583455
getattr(request, x))
34593456
assert request.session
34603457
assert request.config
34613458
def test_func(arg):
34623459
pass
3463-
""" # noqa: UP031 (python syntax issues)
3464-
% (scope, ok.split(), error.split())
3460+
"""
34653461
)
34663462
reprec = pytester.inline_run()
34673463
reprec.assertoutcome(passed=1)

0 commit comments

Comments
 (0)