diff --git a/AUTHORS b/AUTHORS index 2f50a2950..0395feceb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -297,6 +297,7 @@ Ram Rachum Ran Benita Raphael Castaneda Raphael Pierzina +Rafal Semik Raquel Alegre Ravi Chandra Robert Holt diff --git a/changelog/10710.improvement.rst b/changelog/10710.improvement.rst new file mode 100644 index 000000000..a893eec02 --- /dev/null +++ b/changelog/10710.improvement.rst @@ -0,0 +1 @@ +Added ``start`` and ``stop`` timestamps to ``TestReport`` objects. diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index b49dfb32a..a8fa61787 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -262,6 +262,8 @@ class TestReport(BaseReport): when: "Literal['setup', 'call', 'teardown']", sections: Iterable[Tuple[str, str]] = (), duration: float = 0, + start: float = 0, + stop: float = 0, user_properties: Optional[Iterable[Tuple[str, object]]] = None, **extra, ) -> None: @@ -299,6 +301,11 @@ class TestReport(BaseReport): #: Time it took to run just the test. self.duration: float = duration + #: The system time when the call started, in seconds since the epoch. + self.start: float = start + #: The system time when the call ended, in seconds since the epoch. + self.stop: float = stop + self.__dict__.update(extra) def __repr__(self) -> str: @@ -317,6 +324,8 @@ class TestReport(BaseReport): # Remove "collect" from the Literal type -- only for collection calls. assert when != "collect" duration = call.duration + start = call.start + stop = call.stop keywords = {x: 1 for x in item.keywords} excinfo = call.excinfo sections = [] @@ -361,6 +370,8 @@ class TestReport(BaseReport): when, sections, duration, + start, + stop, user_properties=item.user_properties, ) diff --git a/testing/test_reports.py b/testing/test_reports.py index 31b6cf1af..e101b51da 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -6,6 +6,7 @@ from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr from _pytest.config import Config from _pytest.pytester import Pytester +from _pytest.python_api import approx from _pytest.reports import CollectReport from _pytest.reports import TestReport @@ -415,6 +416,26 @@ class TestReportSerialization: result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"]) result.stdout.no_fnmatch_line("ERROR - *ConftestImportFailure*") + def test_report_timestamps_match_duration(self, pytester: Pytester, mock_timing): + reprec = pytester.inline_runsource( + """ + import pytest + from _pytest import timing + @pytest.fixture + def fixture_(): + timing.sleep(5) + yield + timing.sleep(5) + def test_1(fixture_): timing.sleep(10) + """ + ) + reports = reprec.getreports("pytest_runtest_logreport") + assert len(reports) == 3 + for report in reports: + data = report._to_json() + loaded_report = TestReport._from_json(data) + assert loaded_report.stop - loaded_report.start == approx(report.duration) + class TestHooks: """Test that the hooks are working correctly for plugins"""