失败重试


方式一:源码扩展

使源代码增加--retry选项,实现Test Case级别的失败用例自动再执行:失败用例会重跑N次(默认3次),直至成功或 耗尽重试次数,生成的日志和报告文件中只会体现最后一次执行的结果。

修改代码如下:

Python Home = C:\Python27

Robot Framework库路径为${Python Home}\Lib\site-packages

1、修改robot库中run.py,文件路径为${Python Home}\Lib\site-packages\robot\run.py

  • 增加编码注释
# -*-coding:utf-8 -*-
  • 引入minidom模块,并设置系统编码
from xml.dom import minidom


reload(sys)
sys.setdefaultencoding('UTF-8')
  • 在USAGE的Options中增加retry指令说明
 -Y --retry retry         Set the retry times if test failed.
  • RobotFramework类中增加局部方法_make()
class RobotFramework(Application):

    def _make(self,outxml):
        xmldoc = minidom.parse(outxml)
        suiteElementList = xmldoc.getElementsByTagName('suite')
        mySuite = []
        for suiteElement in suiteElementList:
            if suiteElement.childNodes is not None:
                for element in suiteElement.childNodes:
                    if element.nodeName == 'test':
                        mySuite.append(suiteElement)
                        break
        for suite in mySuite:
            testElements = {}
            for element in suite.childNodes:
                if element.nodeName == 'test':
                    name = element.getAttribute('name')
                    if testElements.get(name) == None:
                        testElements.update({name:[element]})
                    else:
                        testElements.get(name).append(element)
            for n,el in testElements.iteritems():
                for i in el[0:-1]:
                    textElement = i.nextSibling
                    suite.removeChild(i)
                    suite.removeChild(textElement)
        savefile = open(outxml,'w')
        root = xmldoc.documentElement
        root.writexml(savefile)
        savefile.close()
  • RobotFramework类中main()方法调用_make()
#RobotFramwwork类完整代码如下

class RobotFramework(Application):

    def __init__(self):
        Application.__init__(self, USAGE, arg_limits=(1,),
                             env_options='ROBOT_OPTIONS', logger=LOGGER)

    def main(self, datasources, **options):
        settings = RobotSettings(options)
        LOGGER.register_console_logger(**settings.console_output_config)
        LOGGER.info('Settings:\n%s' % unic(settings))
        suite = TestSuiteBuilder(settings['SuiteNames'],
                                 settings['WarnOnSkipped'],
                                 settings['Extension']).build(*datasources)
        suite.configure(**settings.suite_config)
        if settings.pre_run_modifiers:
            suite.visit(ModelModifier(settings.pre_run_modifiers,
                                      settings.run_empty_suite, LOGGER))
        with pyloggingconf.robot_handler_enabled(settings.log_level):
            result = suite.run(settings)
            LOGGER.info("Tests execution ended. Statistics:\n%s"
                        % result.suite.stat_message)
            self._make(settings.output)
            if settings.log or settings.report or settings.xunit:
                writer = ResultWriter(settings.output if settings.log
                                      else result)
                writer.write_results(settings.get_rebot_settings())
        return result.return_code

    def validate(self, options, arguments):
        return self._filter_options_without_value(options), arguments

    def _filter_options_without_value(self, options):
        return dict((name, value) for name, value in options.items()
                    if value not in (None, []))

    def _make(self,outxml):
        xmldoc = minidom.parse(outxml)
        suiteElementList = xmldoc.getElementsByTagName('suite')
        mySuite = []
        for suiteElement in suiteElementList:
            if suiteElement.childNodes is not None:
                for element in suiteElement.childNodes:
                    if element.nodeName == 'test':
                        mySuite.append(suiteElement)
                        break
        for suite in mySuite:
            testElements = {}
            for element in suite.childNodes:
                if element.nodeName == 'test':
                    name = element.getAttribute('name')
                    if testElements.get(name) == None:
                        testElements.update({name:[element]})
                    else:
                        testElements.get(name).append(element)
            for n,el in testElements.iteritems():
                for i in el[0:-1]:
                    textElement = i.nextSibling
                    suite.removeChild(i)
                    suite.removeChild(textElement)
        savefile = open(outxml,'w')
        root = xmldoc.documentElement
        root.writexml(savefile)
        savefile.close()

2、修改robot库中settings.py,文件路径为${Python Home}\Lib\site-packages\robot\conf\settings.py

  • 在RobotSettings类中的_extra_cli_opts字典内增加Retry项
class RobotSettings(_BaseSettings):
    _extra_cli_opts = {'Extension'          : ('extension', None),
                       'Output'             : ('output', 'output.xml'),
                       'LogLevel'           : ('loglevel', 'INFO'),
                       'DryRun'             : ('dryrun', False),
                       'ExitOnFailure'      : ('exitonfailure', False),
                       'ExitOnError'        : ('exitonerror', False),
                       'SkipTeardownOnExit' : ('skipteardownonexit', False),
                       'Randomize'          : ('randomize', 'NONE'),
                       'RunEmptySuite'      : ('runemptysuite', False),
                       'Retry'              : ('retry',3),
                       'WarnOnSkipped'      : ('warnonskippedfiles', False),
                       'Variables'          : ('variable', []),
                       'VariableFiles'      : ('variablefile', []),

3、修改robot库中itemlist.py,文件路径为${Python Home}\Lib\site-packages\robot\model\itemlist.py

  • 修改visit()方法
    # def visit(self, visitor):
    #     for item in self._items:
    #         item.visit(visitor)

    def visit(self, visitor):
        for item in self:
            if self.__module__ == 'robot.model.testcase' and hasattr(visitor,"_context"):
                testStatus = ''
                for i in range(0,int(visitor._settings._opts['Retry'])):
                    if testStatus != 'PASS':
                        if item.name in visitor._executed_tests:
                            visitor._executed_tests.pop(item.name)
                        item.visit(visitor)
                        testStatus = visitor._context.variables['${PREV_TEST_STATUS}']
                    else:
                        break
            else:
                item.visit(visitor)

4、修改robotide库中usages.py,文件路径为${Python Home}\Lib\site-packages\robotide\contrib\testrunner\usages.py

  • 在USAGE的Options中增加retry指令说明
-Y --retry retry Set the retry times if test failed.

方式二:自带功能

Robot Framework能够从上一次失败的output.xml文件中找出失败的Test Case然后再次执行。

 -R --rerunfailed output  Select failed tests from an earlier output file to be
                          re-executed. Equivalent to selecting same tests
                          individually using --test option.
 -S --rerunfailedsuites output  Select failed suite from an earlier output file
                          to be re-executed. New in RF 3.0.1.

-R 基于Test Case;-S 基于Test Suite。

Robot Framework的rebot组件可以合并测试结果。

 -R --merge               When combining results, merge outputs together
                          instead of putting them under a new top level suite.
                          Example: rebot --merge orig.xml rerun.xml

使用逻辑:

  • 运行测试用例
robot -d testReport1 testDir
  • 调用-R或-S运行第一次失败的用例
robot -d testReport2 -R testReport1\output.xml testDir
  • 运用rebot组件合并测试结果
#合并测试结果,并将最终结果存放于testReport3
rebot -d testReport3 -R testReport1\output.xml testReport2\output.xml
#将最后一次的截图存放到testReport3
copy testReport2\*.png testReport3

results matching ""

    No results matching ""