我在 python 的 unittest2 框架中运行了一些基于类的单元测试。我们正在使用 Selenium WebDriver,它有一个方便的save_screenshot()
方法。我想为每个测试失败在 tearDown() 中截取屏幕截图,以减少调试测试失败原因所花费的时间。
但是,我找不到任何仅在测试失败时运行代码的方法。tearDown()
无论测试是否成功,都会调用它,而且我不想用数百个浏览器屏幕截图来混淆我们的文件系统,以便测试成功。
你会如何处理这个问题?
我在 python 的 unittest2 框架中运行了一些基于类的单元测试。我们正在使用 Selenium WebDriver,它有一个方便的save_screenshot()
方法。我想为每个测试失败在 tearDown() 中截取屏幕截图,以减少调试测试失败原因所花费的时间。
但是,我找不到任何仅在测试失败时运行代码的方法。tearDown()
无论测试是否成功,都会调用它,而且我不想用数百个浏览器屏幕截图来混淆我们的文件系统,以便测试成功。
你会如何处理这个问题?
找到了解决方案 - 我可以覆盖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,但到目前为止它似乎有效。
这是与@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
这实际上是在这个博客中找到的。
我进一步扩展了它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'
如果您有额外的包装器来运行所有脚本(如基类),这将特别有用。
覆盖fail()
以生成屏幕截图,然后调用TestCase.fail(self)
?
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)
在每个测试周围使用装饰器。
记住装饰新测试或避免返回并装饰一堆现有测试的最安全方法是使用元类来包装所有测试功能。如何包装类的每个方法?答案提供了您需要的基础知识。
您可能应该过滤仅包含测试的函数,例如:
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)