捕获复杂测试套件中测试失败的屏幕截图

时间:2017-01-19 08:50:11

标签: python selenium robotframework

当我们的一些Robot Framework前端测试失败时,我想设置一个很好的方法来捕获屏幕截图。我当然可以补充一下:

Run Keyword If Test Failed    Capture Page Screenshot

要测试Teardown,但考虑到我有大量复杂的测试套件,包含数百个测试和嵌套结构 - 我需要将其添加到这么多Teardowns中,这对我来说似乎很难看。

我做了一些实验。我认为前进的方向是使用listener。所以我尝试了这个:

class ExtendedSelenium(Selenium2Library):
    ROBOT_LISTENER_API_VERSION = 3

    def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot', screenshot_root_directory=None):
        super(ExtendedSelenium, self).__init__(timeout, implicit_wait, run_on_failure, screenshot_root_directory)
        self.ROBOT_LIBRARY_LISTENER = self

    def _end_test(self, data, result):
        if not result.passed:
            screenshot_name = data.id + data.name.replace(" ", "_") + ".png"
            try:
                self.capture_page_screenshot(screenshot_name)
            except:
                pass

它捕获屏幕截图,但图片在日志中不可见。我能够在测试消息中显示它,在捕获后添加此步骤:

BuiltIn().set_test_message("*HTML* {} <br/> <img src={}>".format(result.message, screenshot_name))

但是,它仍然不是最好的。

然后我尝试使用Visitor接口(与--prerunmodifier一起使用)采用不同的方法:

from robot.api import SuiteVisitor


class CaptureScreenshot(SuiteVisitor):

    def end_test(self, test):
        if test.status == 'FAIL':
            test.keywords.create('Capture Page Screenshot', type='teardown')

但它会用新的替换任何现有的测试Teardown(只有一个关键字'Capture Page Screenshot')。我想我可以通过添加Teardowns关键字来修改现有的Capture,但我不是。

有没有漂亮,干净,pythonic的方式来做到这一点?我错过了什么吗?

2 个答案:

答案 0 :(得分:1)

最后,它最终得到了如下的库监听器。我不得不同意Bryan:代码并不简短,但是它符合预期的目标 - 套件中的单点定义了截屏捕获。 作为一个很大的优势,我认为有可能捕获失败设置的屏幕截图 - 在某些情况下,它有助于我们识别可能的基础设施问题。 请注意ActionChains部分 - 它会在浏览器中缩小。我们的前端应用程序使用部分页面滚动,通过此缩放,我们可以在该滚动内部看到更多内容,这对我们非常有帮助。此ActionChains的结果因浏览器而异,因此这是真正的解决方法。

#!/usr/bin/python
# -*- coding: utf-8 -*-
from Selenium2Library import Selenium2Library
from selenium.common.exceptions import StaleElementReferenceException, WebDriverException
import re


class ExtendedSelenium(Selenium2Library):
    """ Robot Framework library extending Robot Framework Selenium2Library library.
    """
    ROBOT_LISTENER_API_VERSION = 2
    DON_NOT_CAPTURE_KEYWORDS = ["Run Keyword And Ignore Error", "Run Keyword And Expect Error", "Run Keyword And Return Status", "Wait Until.*"]

    def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='', screenshot_root_directory=None):
        super(ExtendedSelenium, self).__init__(timeout, implicit_wait, run_on_failure, screenshot_root_directory)
        self.ROBOT_LIBRARY_LISTENER = self
        self._is_current_keyword_inside_teardown = False
        self._do_not_capture_parent_keywords_count = 0
        self._screenshot_was_captured = False

    def _start_test(self, name, attributes):
        """ Reset flags at the begin of each test.
        """
        self._do_not_capture_parent_keywords_count = 0
        self._is_current_keyword_inside_teardown = False
        self._screenshot_was_captured = False

    def _start_keyword(self, name, attributes):
        """ Set keyword flag at the beginning of teardown.
        If the keyword is one of the 'do not capture keywords' increase _do_not_capture_parent_keywords_count counter.
        """
        if attributes["type"] == "Teardown":
            self._is_current_keyword_inside_teardown = True
        if any(kw for kw in self.DON_NOT_CAPTURE_KEYWORDS if re.match(kw, attributes["kwname"])):
            self._do_not_capture_parent_keywords_count += 1

    def _end_keyword(self, name, attributes):
        """If the keyword is one of the 'do not capture keywords' decrease _do_not_capture_parent_keywords_count counter.
        Capture Screenshot if:
        - keyword failed AND
        - test is not in teardown phase AND
        - the parent keyword isn't one of the 'do not capture keywords'
        RuntimeError exception is thrown when no browser is open (backend test), no screenshot is captured in this case.
        """
        if any(kw for kw in self.DON_NOT_CAPTURE_KEYWORDS if re.match(kw, attributes["kwname"])):
            self._do_not_capture_parent_keywords_count -= 1
        if not attributes["status"] == "PASS" and not self._is_current_keyword_inside_teardown and self._do_not_capture_parent_keywords_count == 0 and not self._screenshot_was_captured:
            self._screenshot_was_captured = True
            try:
                self.capture_page_screenshot()
                # TODO refactor this so it is reusable and nice!
                from selenium.webdriver.common.action_chains import ActionChains
                from selenium.webdriver.common.keys import Keys
                ActionChains(super(ExtendedSelenium, self)._current_browser()).send_keys(Keys.CONTROL, Keys.SUBTRACT, Keys.NULL).perform()
                ActionChains(super(ExtendedSelenium, self)._current_browser()).send_keys(Keys.CONTROL, Keys.SUBTRACT, Keys.NULL).perform()
                self.capture_page_screenshot()
                ActionChains(super(ExtendedSelenium, self)._current_browser()).send_keys(Keys.CONTROL, '0', Keys.NULL).perform()
            except RuntimeError:
                pass

欢迎任何评论。

答案 1 :(得分:0)

我将所有内容都包含在try / except块中以供我的测试使用。那样在任何时候,如果测试失败,屏幕截图都在except块中,我会在/之前/之后得到一个屏幕截图...等等。

try:
    class UnitTestRunner():
        ...
except:
    driver.save_screenshot('filepath')