@@ -340,6 +340,153 @@ def test_foo(self):
340
340
["collected 1 item" , "* 3 xfailed, 1 passed in *" ]
341
341
)
342
342
343
+ @pytest .mark .parametrize ("runner" , ["unittest" , "pytest-normal" , "pytest-xdist" ])
344
+ def test_skip_with_failure (
345
+ self ,
346
+ pytester : pytest .Pytester ,
347
+ monkeypatch : pytest .MonkeyPatch ,
348
+ runner : Literal ["unittest" , "pytest-normal" , "pytest-xdist" ],
349
+ ) -> None :
350
+ monkeypatch .setenv ("COLUMNS" , "200" )
351
+ p = pytester .makepyfile (
352
+ """
353
+ import pytest
354
+ from unittest import expectedFailure, TestCase, main
355
+
356
+ class T(TestCase):
357
+ def test_foo(self):
358
+ for i in range(10):
359
+ with self.subTest("custom message", i=i):
360
+ if i < 4:
361
+ self.skipTest(f"skip subtest i={i}")
362
+ assert i < 4
363
+
364
+ if __name__ == '__main__':
365
+ main()
366
+ """
367
+ )
368
+ if runner == "unittest" :
369
+ result = pytester .runpython (p )
370
+ if sys .version_info < (3 , 11 ):
371
+ result .stderr .re_match_lines (
372
+ [
373
+ "FAIL: test_foo \(__main__\.T\) \[custom message\] \(i=4\).*" ,
374
+ "FAIL: test_foo \(__main__\.T\) \[custom message\] \(i=9\).*" ,
375
+ "Ran 1 test in .*" ,
376
+ "FAILED \(failures=6, skipped=4\)" ,
377
+ ]
378
+ )
379
+ else :
380
+ result .stderr .re_match_lines (
381
+ [
382
+ "FAIL: test_foo \(__main__\.T\.test_foo\) \[custom message\] \(i=4\).*" ,
383
+ "FAIL: test_foo \(__main__\.T\.test_foo\) \[custom message\] \(i=9\).*" ,
384
+ "Ran 1 test in .*" ,
385
+ "FAILED \(failures=6, skipped=4\)" ,
386
+ ]
387
+ )
388
+ elif runner == "pytest-normal" :
389
+ result = pytester .runpytest (p , "-v" , "-rsf" )
390
+ result .stdout .re_match_lines (
391
+ [
392
+ r"test_skip_with_failure.py::T::test_foo \[custom message\] \(i=0\) SUBSKIP \(skip subtest i=0\) .*" ,
393
+ r"test_skip_with_failure.py::T::test_foo \[custom message\] \(i=3\) SUBSKIP \(skip subtest i=3\) .*" ,
394
+ r"test_skip_with_failure.py::T::test_foo \[custom message\] \(i=4\) SUBFAIL .*" ,
395
+ r"test_skip_with_failure.py::T::test_foo \[custom message\] \(i=9\) SUBFAIL .*" ,
396
+ "test_skip_with_failure.py::T::test_foo PASSED .*" ,
397
+ "[custom message] (i=0) SUBSKIP [1] test_skip_with_failure.py:5: skip subtest i=0" ,
398
+ "[custom message] (i=0) SUBSKIP [1] test_skip_with_failure.py:5: skip subtest i=3" ,
399
+ "[custom message] (i=4) SUBFAIL test_skip_with_failure.py::T::test_foo - AssertionError: assert 4 < 4" ,
400
+ "[custom message] (i=9) SUBFAIL test_skip_with_failure.py::T::test_foo - AssertionError: assert 9 < 4" ,
401
+ ".* 6 failed, 1 passed, 4 skipped in .*" ,
402
+ ]
403
+ )
404
+ else :
405
+ pytest .xfail ("Not producing the expected results (#5)" )
406
+ result = pytester .runpytest (p ) # type:ignore[unreachable]
407
+ result .stdout .fnmatch_lines (
408
+ ["collected 1 item" , "* 3 skipped, 1 passed in *" ]
409
+ )
410
+
411
+ @pytest .mark .parametrize ("runner" , ["unittest" , "pytest-normal" , "pytest-xdist" ])
412
+ def test_skip_with_failure_and_non_subskip (
413
+ self ,
414
+ pytester : pytest .Pytester ,
415
+ monkeypatch : pytest .MonkeyPatch ,
416
+ runner : Literal ["unittest" , "pytest-normal" , "pytest-xdist" ],
417
+ ) -> None :
418
+ monkeypatch .setenv ("COLUMNS" , "200" )
419
+ p = pytester .makepyfile (
420
+ """
421
+ import pytest
422
+ from unittest import expectedFailure, TestCase, main
423
+
424
+ class T(TestCase):
425
+ def test_foo(self):
426
+ for i in range(10):
427
+ with self.subTest("custom message", i=i):
428
+ if i < 4:
429
+ self.skipTest(f"skip subtest i={i}")
430
+ assert i < 4
431
+ self.skipTest(f"skip the test")
432
+
433
+ if __name__ == '__main__':
434
+ main()
435
+ """
436
+ )
437
+ if runner == "unittest" :
438
+ result = pytester .runpython (p )
439
+ if sys .version_info < (3 , 11 ):
440
+ result .stderr .re_match_lines (
441
+ [
442
+ "FAIL: test_foo \(__main__\.T\) \[custom message\] \(i=4\).*" ,
443
+ "FAIL: test_foo \(__main__\.T\) \[custom message\] \(i=9\).*" ,
444
+ "Ran 1 test in .*" ,
445
+ "FAILED \(failures=6, skipped=5\)" ,
446
+ ]
447
+ )
448
+ else :
449
+ result .stderr .re_match_lines (
450
+ [
451
+ "FAIL: test_foo \(__main__\.T\.test_foo\) \[custom message\] \(i=4\).*" ,
452
+ "FAIL: test_foo \(__main__\.T\.test_foo\) \[custom message\] \(i=9\).*" ,
453
+ "Ran 1 test in .*" ,
454
+ "FAILED \(failures=6, skipped=5\)" ,
455
+ ]
456
+ )
457
+ elif runner == "pytest-normal" :
458
+ result = pytester .runpytest (p , "-v" , "-rsf" )
459
+ # The `(i=0)` is not correct but it's given by pytest `TerminalReporter` without `--no-fold-skipped`
460
+ result .stdout .re_match_lines (
461
+ [
462
+ r"test_skip_with_failure_and_non_subskip.py::T::test_foo \[custom message\] \(i=4\) SUBFAIL .*" ,
463
+ r"test_skip_with_failure_and_non_subskip.py::T::test_foo SKIPPED \(skip the test\)" ,
464
+ r"\[custom message\] \(i=0\) SUBSKIP \[1\] test_skip_with_failure_and_non_subskip.py:5: skip subtest i=3" ,
465
+ r"\[custom message\] \(i=0\) SUBSKIP \[1\] test_skip_with_failure_and_non_subskip.py:5: skip the test" ,
466
+ r"\[custom message\] \(i=4\) SUBFAIL test_skip_with_failure_and_non_subskip.py::T::test_foo" ,
467
+ r".* 6 failed, 5 skipped in .*" ,
468
+ ]
469
+ )
470
+ # check with `--no-fold-skipped` (which gives the correct information)
471
+ if sys .version_info >= (3 , 10 ):
472
+ result = pytester .runpytest (p , "-v" , "--no-fold-skipped" , "-rsf" )
473
+ result .stdout .re_match_lines (
474
+ [
475
+ r"test_skip_with_failure_and_non_subskip.py::T::test_foo \[custom message\] \(i=4\) SUBFAIL .*" ,
476
+ r"test_skip_with_failure_and_non_subskip.py::T::test_foo SKIPPED \(skip the test\).*" ,
477
+ r"\[custom message\] \(i=3\) SUBSKIP test_skip_with_failure_and_non_subskip.py::T::test_foo - Skipped: skip subtest i=3" ,
478
+ r"SKIPPED test_skip_with_failure_and_non_subskip.py::T::test_foo - Skipped: skip the test" ,
479
+ r"\[custom message\] \(i=4\) SUBFAIL test_skip_with_failure_and_non_subskip.py::T::test_foo" ,
480
+ r".* 6 failed, 5 skipped in .*" ,
481
+ ]
482
+ )
483
+ else :
484
+ pytest .xfail ("Not producing the expected results (#5)" )
485
+ result = pytester .runpytest (p ) # type:ignore[unreachable]
486
+ result .stdout .fnmatch_lines (
487
+ ["collected 1 item" , "* 3 skipped, 1 passed in *" ]
488
+ )
489
+
343
490
344
491
class TestCapture :
345
492
def create_file (self , pytester : pytest .Pytester ) -> None :
0 commit comments