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

后端 未结 5 2104
既然无缘
既然无缘 2020-12-06 11:01

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

相关标签:
5条回答
  • 2020-12-06 11:46

    Override fail() to generate the screenshot and then call TestCase.fail(self)?

    0 讨论(0)
  • 2020-12-06 11:49

    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.

    0 讨论(0)
  • 2020-12-06 11:55

    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.

    0 讨论(0)
  • 2020-12-06 11:55

    sys.exc_info() should give you exit information on whether a test failed or not. So something like this:

    def tearDown(self):
        if sys.exc_info()[0]:
            path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../failures', self.driver.browser)
            if not os.path.exists(path):
                try:
                    os.makedirs(path)
                except Exception:
                    # Since this might not be thread safe
                    pass
            filename = '%s.%s.png' % (self.__class__.__name__, self._testMethodName)
            file_path = os.path.join(path, filename)
            self.driver.get_screenshot_as_file(file_path)
    
    0 讨论(0)
  • 2020-12-06 11:56

    Use a decorator around each test.

    The safest way to remember to decorate new tests, or to avoid going back and decorating a bunch of existing tests, is to use a metaclass to wrap all of the test functions. The How to wrap every method of a class? answer provides the basics of what you need.

    You probably should filter the functions that are wrapped down to just the tests, e.g.:

    class ScreenshotMetaClass(type):
        """Wraps all tests with screenshot_on_error"""
        def __new__(meta, classname, bases, classDict):
            newClassDict = {}
            for attributeName, attribute in classDict.items():
                if type(attribute) == FunctionType and 'test' in attributeName.lower():
                    # replace the function with a wrapped version
                    attribute = screenshot_on_error(attribute)
                newClassDict[attributeName] = attribute
            return type.__new__(meta, classname, bases, newClassDict)
    
    0 讨论(0)
自定义标题
段落格式
字体
字号
代码语言
提交回复
热议问题