Search code examples
pythonscreenshotpython-unittestsyschrome-web-driver

Cannot react to exception in teardown


I'm trying to add something to my teardown method so that in the event of an exception it takes a screenshot before closing the browser instance.

So far I have: -

def tearDown(self):
    if sys.exc_info()[0]:
        test_method_name = self._testMethodName
        screenshot_name = common_functions.create_unique_id() + test_method_name + ".png"
        common_functions.take_screenshot(self.driver, screenshot_name)
    self.driver.close()

As it is the screenshot is never taken and if I change the if statement to if sys.exc_info(): then it always takes a screenshot regardless of whether an exception is raised.

When I query what is returned by sys.exc_info I get None, None, None. I was hoping that the first element should contain the exception name at least.


Solution

  • From the docs on sys.exc_info() (my emphasis):

    This function returns a tuple of three values that give information about the exception that is currently being handled.

    This is not good enough for what you're looking for because when tearDown is called, the potential exception that occurred during the test has already been handled, that's why regardless of whether or not an exception has been raised during the test, sys.exc_info() will return a tuple of None, None, None.

    However, you can try using a different approach: in setUp define a flag had_exception which will indicate whether or not the test has had an exception. This will look something like:

    class MyTest(unittest.TestCase):
        def setUp(self):
            self.had_exception = True
            # ...
    
        def test_sample(self):
            self.assertTrue("your test logic here...")
            self.had_exception = False
    
        def tearDown(self):
            if self.had_exception:
                test_method_name = self._testMethodName
                screenshot_name = common_functions.create_unique_id() + test_method_name + ".png"
                common_functions.take_screenshot(self.driver, screenshot_name)
            self.driver.close()