From 86c80dae4a75bd4227881026aaa119a7f2d86feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Wed, 3 Jul 2024 14:54:33 +0200 Subject: [PATCH] feat: add --fold-skipped=yes|no cli option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This controlls how the skipped tests are display in the short summary. - yes (default): keeps the current behavior that folds the skipped tests together - no: each skipped test is on its own line, display as any other status Resolves: https://github.com/pytest-dev/pytest/issues/9876 Signed-off-by: Pavel Březina --- changelog/12567.feature.rst | 7 +++++++ doc/en/how-to/output.rst | 5 +++++ src/_pytest/terminal.py | 35 +++++++++++++++++++++++++++++++++- testing/test_terminal.py | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 changelog/12567.feature.rst diff --git a/changelog/12567.feature.rst b/changelog/12567.feature.rst new file mode 100644 index 00000000000..3690d7aff68 --- /dev/null +++ b/changelog/12567.feature.rst @@ -0,0 +1,7 @@ +Added ``--no-fold-skipped`` command line option + +If this option is set, then skipped tests in short summary are no longer grouped +by reason but all tests are printed individually with correct nodeid in the same +way as other statuses. + +-- by :user:`pbrezina` diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst index 7a4e32edc78..4994ad1af69 100644 --- a/doc/en/how-to/output.rst +++ b/doc/en/how-to/output.rst @@ -552,6 +552,11 @@ captured output: PASSED test_example.py::test_ok == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === +.. note:: + + By default, parametrized variants of skipped tests are grouped together if + they share the same skip reason. You can use ``--no-fold-skipped`` to print each skipped test separately. + Creating resultlog format files -------------------------------------------------- diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index f364316e2e2..8c722124d04 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -154,6 +154,13 @@ def pytest_addoption(parser: Parser) -> None: dest="no_summary", help="Disable summary", ) + group._addoption( + "--no-fold-skipped", + action="store_false", + dest="fold_skipped", + default=True, + help="Do not fold skipped tests in short summary.", + ) group._addoption( "-q", "--quiet", @@ -371,6 +378,7 @@ def __init__(self, config: Config, file: TextIO | None = None) -> None: self._screen_width = self._tw.fullwidth self.currentfspath: None | Path | str | int = None self.reportchars = getreportopt(config) + self.foldskipped = config.option.fold_skipped self.hasmarkup = self._tw.hasmarkup self.isatty = file.isatty() self._progress_nodeids_reported: set[str] = set() @@ -1232,7 +1240,7 @@ def show_xpassed(lines: list[str]) -> None: line += " - " + str(reason) lines.append(line) - def show_skipped(lines: list[str]) -> None: + def show_skipped_folded(lines: list[str]) -> None: skipped: list[CollectReport] = self.stats.get("skipped", []) fskips = _folded_skips(self.startpath, skipped) if skipped else [] if not fskips: @@ -1252,6 +1260,31 @@ def show_skipped(lines: list[str]) -> None: else: lines.append("%s [%d] %s: %s" % (markup_word, num, fspath, reason)) + def show_skipped_unfolded(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get("skipped", []) + + for rep in skipped: + assert rep.longrepr is not None + assert isinstance(rep.longrepr, tuple), (rep, rep.longrepr) + assert len(rep.longrepr) == 3, (rep, rep.longrepr) + + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.longrepr[2] + if reason: + line += " - " + str(reason) + lines.append(line) + + def show_skipped(lines: list[str]) -> None: + if self.foldskipped: + show_skipped_folded(lines) + else: + show_skipped_unfolded(lines) + REPORTCHAR_ACTIONS: Mapping[str, Callable[[list[str]], None]] = { "x": show_xfailed, "X": show_xpassed, diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 5e3f631e22b..11ad623fb6b 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1150,6 +1150,44 @@ def test(): result.stdout.fnmatch_lines([expected]) assert result.stdout.lines.count(expected) == 1 + def test_summary_s_folded(self, pytester: Pytester) -> None: + """Test that skipped tests are correctly folded""" + pytester.makepyfile( + """ + import pytest + + @pytest.mark.parametrize("param", [True, False]) + @pytest.mark.skip("Some reason") + def test(param): + pass + """ + ) + result = pytester.runpytest("-rs") + expected = "SKIPPED [2] test_summary_s_folded.py:3: Some reason" + result.stdout.fnmatch_lines([expected]) + assert result.stdout.lines.count(expected) == 1 + + def test_summary_s_unfolded(self, pytester: Pytester) -> None: + """Test that skipped tests are not folded if --no-fold-skipped is set""" + pytester.makepyfile( + """ + import pytest + + @pytest.mark.parametrize("param", [True, False]) + @pytest.mark.skip("Some reason") + def test(param): + pass + """ + ) + result = pytester.runpytest("-rs", "--no-fold-skipped") + expected = [ + "SKIPPED test_summary_s_unfolded.py::test[True] - Skipped: Some reason", + "SKIPPED test_summary_s_unfolded.py::test[False] - Skipped: Some reason", + ] + result.stdout.fnmatch_lines(expected) + assert result.stdout.lines.count(expected[0]) == 1 + assert result.stdout.lines.count(expected[1]) == 1 + @pytest.mark.parametrize( ("use_ci", "expected_message"),