Skip to content

Commit 3065de7

Browse files
patchback[bot]radoeringnicoddemus
authored
subtests: fix inconcistent handling of non-string messages (#14196) (#14199)
Fixes #14195 --------- (cherry picked from commit 8950ca3) Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com> Co-authored-by: Bruno Oliveira <bruno@pytest.org>
1 parent 60fb59d commit 3065de7

4 files changed

Lines changed: 64 additions & 1 deletion

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ Ralf Schmitt
382382
Ralph Giles
383383
Ram Rachum
384384
Ran Benita
385+
Randy Döring
385386
Raphael Castaneda
386387
Raphael Pierzina
387388
Rafal Semik

changelog/14195.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue where non-string messages passed to `unittest.TestCase.subTest()` were not printed.

src/_pytest/unittest.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,10 @@ def addSubTest(
409409
| tuple[type[BaseException], BaseException, TracebackType]
410410
| None,
411411
) -> None:
412+
# Importing this private symbol locally in case this symbol is renamed/removed in the future; importing
413+
# it globally would break pytest entirely, importing it locally only will break unittests using `addSubTest`.
414+
from unittest.case import _subtest_msg_sentinel # type: ignore[attr-defined]
415+
412416
exception_info: ExceptionInfo[BaseException] | None
413417
match exc_info:
414418
case tuple():
@@ -427,7 +431,7 @@ def addSubTest(
427431
when="call",
428432
_ispytest=True,
429433
)
430-
msg = test._message if isinstance(test._message, str) else None # type: ignore[attr-defined]
434+
msg = None if test._message is _subtest_msg_sentinel else str(test._message) # type: ignore[attr-defined]
431435
report = self.ihook.pytest_runtest_makereport(item=self, call=call_info)
432436
sub_report = SubtestReport._new(
433437
report,

testing/test_subtests.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,36 @@ def test_foo(subtests):
370370
)
371371

372372

373+
def test_msg_not_a_string(
374+
pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch
375+
) -> None:
376+
"""
377+
Using a non-string in subtests.test() should still show it in the terminal (#14195).
378+
379+
Note: this was not a problem originally with the subtests fixture, only with TestCase.subTest; this test
380+
was added for symmetry.
381+
"""
382+
monkeypatch.setenv("COLUMNS", "120")
383+
pytester.makepyfile(
384+
"""
385+
def test_int_msg(subtests):
386+
with subtests.test(42):
387+
assert False, "subtest failure"
388+
389+
def test_no_msg(subtests):
390+
with subtests.test():
391+
assert False, "subtest failure"
392+
"""
393+
)
394+
result = pytester.runpytest()
395+
result.stdout.fnmatch_lines(
396+
[
397+
"SUBFAILED[[]42[]] test_msg_not_a_string.py::test_int_msg - AssertionError: subtest failure",
398+
"SUBFAILED(<subtest>) test_msg_not_a_string.py::test_no_msg - AssertionError: subtest failure",
399+
]
400+
)
401+
402+
373403
@pytest.mark.parametrize("flag", ["--last-failed", "--stepwise"])
374404
def test_subtests_last_failed_step_wise(pytester: pytest.Pytester, flag: str) -> None:
375405
"""Check that --last-failed and --step-wise correctly rerun tests with failed subtests."""
@@ -619,6 +649,33 @@ def test_foo(self):
619649
"SUBSKIPPED[[]subtest 1[]] [[]1[]] *.py:*: skip subtest 1"
620650
)
621651

652+
def test_msg_not_a_string(
653+
self, pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch
654+
) -> None:
655+
"""Using a non-string in TestCase.subTest should still show it in the terminal (#14195)."""
656+
monkeypatch.setenv("COLUMNS", "120")
657+
pytester.makepyfile(
658+
"""
659+
from unittest import TestCase
660+
661+
class T(TestCase):
662+
def test_int_msg(self):
663+
with self.subTest(42):
664+
assert False, "subtest failure"
665+
666+
def test_no_msg(self):
667+
with self.subTest():
668+
assert False, "subtest failure"
669+
"""
670+
)
671+
result = pytester.runpytest()
672+
result.stdout.fnmatch_lines(
673+
[
674+
"SUBFAILED[[]42[]] test_msg_not_a_string.py::T::test_int_msg - AssertionError: subtest failure",
675+
"SUBFAILED(<subtest>) test_msg_not_a_string.py::T::test_no_msg - AssertionError: subtest failure",
676+
]
677+
)
678+
622679

623680
class TestCapture:
624681
def create_file(self, pytester: pytest.Pytester) -> None:

0 commit comments

Comments
 (0)