【问题标题】:How to execute code only on test failures with python unittest2?如何使用 python unittest2 仅在测试失败时执行代码?
【发布时间】:2012-09-05 21:53:53
【问题描述】:

我在 python 的 unittest2 框架中运行了一些基于类的单元测试。我们正在使用 Selenium WebDriver,它有一个方便的save_screenshot() 方法。我想为每个测试失败在 tearDown() 中截取屏幕截图,以减少调试为什么测试失败所花费的时间。

但是,我找不到任何仅在测试失败时运行代码的方法。无论测试是否成功,tearDown() 都会被调用,我不想让我们的文件系统被数百个浏览器屏幕截图弄乱,以便测试成功。

你会如何处理这个问题?

【问题讨论】:

    标签: python unit-testing selenium-webdriver


    【解决方案1】:

    找到解决方案 - 我可以覆盖 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
    

    这似乎令人难以置信的hacky,但它似乎到目前为止工作。

    【讨论】:

    • 哇,真是个黑客。一个异常类,包裹在一个函数中,包裹在一个属性中,都是为了将​​TestCase实例获取到异常的__init__()方法中。丑陋,但至少它有效!
    【解决方案2】:

    这是与@craigds answer 类似的方法,但支持目录并更好地兼容 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
    

    这实际上是在这个blog中找到的。

    我用argparse进一步扩展了它:

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

    所以目录可以通过系统变量或传递的参数动态指定:

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

    如果您有额外的包装器来运行所有脚本(如基类),这将特别有用。

    【讨论】:

      【解决方案3】:

      覆盖fail()生成截图然后调用TestCase.fail(self)?

      【讨论】:

      • 这只有在测试用例失败时才有帮助,因为有人叫self.fail()。其他故障,如self.assertTrue(),...等绕过此。
      • 真的吗?我不敢相信他们没有那些电话fail()。当然,您也可以覆盖所有其他方法(叹气)。
      • 有些可以,但有些不可以。似乎有点疯狂;)
      【解决方案4】:

      sys.exc_info() 应该为您提供有关测试是否失败的退出信息。所以是这样的:

      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)
      

      【讨论】:

        【解决方案5】:

        在每个测试周围使用装饰器。

        记住装饰新测试或避免返回并装饰一堆现有测试的最安全方法是使用元类来包装所有测试功能。 How to wrap every method of a class? 答案提供了您需要的基础知识。

        您可能应该过滤仅包含测试的函数,例如:

        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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-02
          • 1970-01-01
          相关资源
          最近更新 更多