Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute code only on test failures with python unittest2?

I have some class-based unit tests running in python's unittest2 framework. We're using Selenium WebDriver, which has a convenient save_screenshot() method. I'd like to grab a screenshot in tearDown() for every test failure, to reduce the time spent debugging why a test failed.

However, I can't find any way to run code on test failures only. tearDown() is called regardless of whether the test succeeds, and I don't want to clutter our filesystem with hundreds of browser screenshots for tests that succeeded.

How would you approach this?

like image 642
craigds Avatar asked Sep 05 '12 21:09

craigds


People also ask

How do you make a test case fail in Python?

assertEqual(first, second, msg=None) Test that first and second are equal. If the values do not compare equal, the test will fail.

How are test failures identified in Python?

If the test fails, an exception will be raised with an explanatory message, and unittest will identify the test case as a failure. Any other exceptions will be treated as errors.

How do you run a test case in Python?

Run all Python tests in a directoryFrom the context menu, select the corresponding run command. If the directory contains tests that belong to the different testing frameworks, select the configuration to be used. For example, select Run 'All tests in: <directory name>' Run pytest in <directory name>'.


2 Answers

Found a solution - I can override failureException:

@property
def failureException(self):
    class MyFailureException(AssertionError):
        def __init__(self_, *args, **kwargs):
            self.b.save_screenshot('%s.png' % self.id())
            return super(MyFailureException, self_).__init__(*args, **kwargs)
    MyFailureException.__name__ = AssertionError.__name__
    return MyFailureException

This seems incredibly hacky but it seems to work so far.

like image 65
craigds Avatar answered Oct 21 '22 22:10

craigds


Here is similar approach to @craigds answer, but with directory support and better compatibility with Python 3:

@property
def failureException(self):
    class MyFailureException(AssertionError):
        def __init__(self_, *args, **kwargs):
            screenshot_dir = 'reports/screenshots'
            if not os.path.exists(screenshot_dir):
                os.makedirs(screenshot_dir)
            self.driver.save_screenshot('{0}/{1}.png'.format(screenshot_dir, self.id()))
            return super(MyFailureException, self_).__init__(*args, **kwargs)
    MyFailureException.__name__ = AssertionError.__name__
    return MyFailureException

This was actually found in this blog.

I've extended it further more with argparse:

parser.add_argument("-r", "--reports-dir", action="store",   dest="dir",      help="Directory to save screenshots.", default="reports")     

so the dir can be specified dynamically either by system variable or passed argument:

screenshot_dir = os.environ.get('REPORTS_DIR', self.args.dir) + '/screenshots'

This is especially useful, if you've additional wrapper to run all your scripts, like a base class.

like image 31
kenorb Avatar answered Oct 21 '22 23:10

kenorb