Skip to content

Instantly share code, notes, and snippets.

@hynekcer
Last active March 17, 2024 00:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hynekcer/1b0a260ef72dae05fe9611904d7b9675 to your computer and use it in GitHub Desktop.
Save hynekcer/1b0a260ef72dae05fe9611904d7b9675 to your computer and use it in GitHub Desktop.
Getting Python's unittest results in a tearDown() method (my SO answer, question 4414234)
"""Getting Python's unittest results in a tearDown() method
https://stackoverflow.com/questions/4414234/getting-pythons-unittest-results-in-a-teardown-method#39606065
"""
import unittest
class MyTest(unittest.TestCase):
def tearDown(self):
if hasattr(self._outcome, 'errors'):
# Python 3.4 - 3.10 (These two methods have no side effects)
result = self.defaultTestResult()
self._feedErrorsToResult(result, self._outcome.errors)
else:
# Python 3.11+
result = self._outcome.result
ok = all(test != self for test, text in result.errors + result.failures)
# Demo output: (print short info immediately - not important)
if ok:
print('\nOK: %s' % (self.id(),))
for typ, errors in (('ERROR', result.errors), ('FAIL', result.failures)):
for test, text in errors:
if test is self:
# the full traceback is in the variable `text`
msg = [x for x in text.split('\n')[1:]
if not x.startswith(' ')][0]
print("\n\n%s: %s\n %s" % (typ, self.id(), msg))
def list2reason(self, exc_list):
if exc_list and exc_list[-1][0] is self:
return exc_list[-1][1]
# demo tests
def test_success(self):
self.assertEqual(1, 1)
def test_fail(self):
self.assertEqual(2, 1)
def test_error(self):
self.assertEqual(1 / 0, 1)
@unittest.expectedFailure
def test_expected_fail(self):
self.assertEqual(2, 1)
@unittest.expectedFailure
def test_expected_error(self):
self.assertEqual(1 / 0, 1)
@unittest.expectedFailure
def test_unexpected_success(self):
self.assertEqual(1, 1)
@fruch
Copy link

fruch commented Jul 24, 2019

@hynekcer this was very helpful

I hope you don't mind me borrowing this 💎

I've changed a bit with extra verbosity, since i'm doing a system test, i.e. I'm running one test at a time, and my tearDowns are too long to get the answers I need:

        if not ok:
            typ, text = ('ERROR', error) if error else ('FAIL', failure)
            TEST_LOG.error("="*70)
            TEST_LOG.error("%s: %s" % (typ, self.id()))
            TEST_LOG.error("-" * 70)
            TEST_LOG.error(text)
            TEST_LOG.error("-" * 70)

@hynekcer
Copy link
Author

Yes I agree with every use of this snippet or my answer on Stackoverflow. This solution can be even extended to extract a traceback (are you interested?), but it was not was not a subject of the question and a short clear code is usually preferred on SO.

@fruch
Copy link

fruch commented Jul 25, 2019

Thanks, mentioned my extra verbosity here and not in SO, for the exact reason, whom ever find this (since it's not linked from SO) might be interested :)

the one downside of this change, it's make my argument of switch to pytest right now a bit less strong 😞
(Since I know how to get this working in pytest, with a few lines in conftest.py)

@hynekcer
Copy link
Author

hynekcer commented Mar 5, 2022

Updated for Python 3.11

@SissiWei123
Copy link

SissiWei123 commented Jun 16, 2022

Hi, I am really interested in extending this to extract a traceback when a test fails

@hynekcer
Copy link
Author

@SissiWei123 It is easy: The full traceback is in the variable text. I added it as a comment to the code

@johnthacker
Copy link

Note that the Python 3.11 approach doesn't work if the tests are run with pytest and the pytest developers have no no intention of making it work, since the _Outcome is a private attribute. The 3.4 to 3.10 code works fine when pytest is used to run unittest based tests.

@fruch
Copy link

fruch commented Jan 6, 2023

Note that the Python 3.11 approach doesn't work if the tests are run with pytest and the pytest developers have no no intention of making it work, since the _Outcome is a private attribute. The 3.4 to 3.10 code works fine when pytest is used to run unittest based tests.

Why do you need this trickery when using pytest as a runner ? pytest offer much better alternatives to this hack

@johnthacker
Copy link

Why do you need this trickery when using pytest as a runner ? pytest offer much better alternatives to this hack

We don't need it, but with Python 3.4-3.10 the original hack works the same both with and without using pytest.

@yzm157
Copy link

yzm157 commented May 11, 2023

In this way, the results of pass, fail, and error can be obtained, but it is impossible to know whether the use case is skipped. is there any way to know

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment