RobotFramework源碼學習(一)


一、啟動RIDE,運行方式是pybot;

二、python安裝目錄C:\Python27\Scripts可以找到pybot.bat文件,該路徑是在環境變量中有配置的;

@echo off
python -m robot.run %*

 pybot.bat文件只兩行代碼,以不打印命令的方式執行該批處理文件;以“python -m robot.run %*”表示以腳本方式運行模塊robot.run,關於腳本方式運行可參考文檔“http://www.pythondoc.com/pythontutorial3/modules.html”。此時 __name__ 被設置為 "__main__";以腳本方式運行模塊robot.run,從入口if __name__ == '__main__':開始。

三、打開robot/run.py,路徑C:\Python27\Lib\site-packages\robot。找到入口函數:

if __name__ == '__main__':
    run_cli(sys.argv[1:])

“python -m robot.run %*”的·“%*”參數傳遞給run_cli函數;

def run_cli(arguments):
    """Command line execution entry point for running tests.

    :param arguments: Command line arguments as a list of strings.

    For programmatic usage the :func:`run` function is typically better. It has
    a better API for that usage and does not call :func:`sys.exit` like this
    function.

    Example::

        from robot import run_cli

        run_cli(['--include', 'tag', 'path/to/tests.html'])
    """
    RobotFramework().execute_cli(arguments)

類 RobotFramework的定義也是在run.py文件中

class RobotFramework(Application):

  類RobotFramework繼承類Application,根據import的內容,可知Application定義在robot.utils模塊里;RobotFramework類獲得了Application類所有的成員方法和成員變量,包括execute_cli方法。通過查看代碼可知,RobotFramework類重寫了父類的main方法。

四、打開robot/utils/application.py,可以查看到execute_cli函數:

    def execute_cli(self, cli_arguments):
        with self._logging():
            options, arguments = self._parse_arguments(cli_arguments)
            rc = self._execute(arguments, options)
        self._exit(rc)

 前面是參數解析,重點是_execute函數:

    def _execute(self, arguments, options):
        try:
            rc = self.main(arguments, **options)
        except DataError, err:
            return self._report_error(unicode(err), help=True)
        except (KeyboardInterrupt, SystemExit):
            return self._report_error('Execution stopped by user.',
                                      rc=STOPPED_BY_USER)
        except:
            error, details = get_error_details()
            return self._report_error('Unexpected error: %s' % error,
                                      details, rc=FRAMEWORK_ERROR)
        else:
            return rc or 0

 在該函數中調用了main方法,返回變量rc,上面提及RobotFramework類重寫了父類的main方法,代碼如下:前后都是日志的設置和打印,關鍵是中間三行:

(一)創建TestBuilder對象,執行build方法得到測試套件suite;

(二)測試套件設置;

(三)測試套件執行得到執行結果;

def main(self, datasources, **options):
        settings = RobotSettings(options)
        LOGGER.register_console_logger(width=settings['MonitorWidth'],
                                       colors=settings['MonitorColors'],
                                       markers=settings['MonitorMarkers'],
                                       stdout=settings['StdOut'],
                                       stderr=settings['StdErr'])
        LOGGER.info('Settings:\n%s' % unicode(settings))
        suite = TestSuiteBuilder(settings['SuiteNames'],
                                 settings['WarnOnSkipped'],
                                 settings['RunEmptySuite']).build(*datasources)
        suite.configure(**settings.suite_config)
        result = suite.run(settings)
        LOGGER.info("Tests execution ended. Statistics:\n%s"
                    % result.suite.stat_message)
        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

(一)創建TestBuilder對象,執行build方法得到測試套件suite。

打開robot/running/builder.py,查看TestSuiteBuilder代碼,來看下測試套件是怎么樣構建的。重點是這四個函數:

TestSuite由TestData構建,在TestData中通過Table數據結構,保存了用例(testcase_table),關鍵字(keyword_table),變量(variable_table),設置(setting_table),這里面還有一些比較細的東西。

    def build(self, *paths):
        if not paths:
            raise DataError('One or more source paths required.')
        if len(paths) == 1:
            return self._build_and_check_if_empty(paths[0])
        root = TestSuite()
        for path in paths:
            root.suites.append(self._build_and_check_if_empty(path))
        return root
    def _build_and_check_if_empty(self, path):
        builded = self._build_suite(self._parse(path))
        if not self._empty_suites_allowed and not builded.test_count:
                raise DataError("Suite '%s' contains no tests." % builded.name)
        builded.remove_empty_suites()
        return builded
    def _build_suite(self, data, parent_defaults=None):
        defaults = TestDefaults(data.setting_table, parent_defaults)
        suite = TestSuite(name=data.name,
                          source=data.source,
                          doc=unicode(data.setting_table.doc),
                          metadata=self._get_metadata(data.setting_table))
        for import_data in data.setting_table.imports:
            self._create_import(suite, import_data)
        self._create_setup(suite, data.setting_table.suite_setup)
        self._create_teardown(suite, data.setting_table.suite_teardown)
        for var_data in data.variable_table.variables:
            self._create_variable(suite, var_data)
        for uk_data in data.keyword_table.keywords:
            self._create_user_keyword(suite, uk_data)
        for test_data in data.testcase_table.tests:
            self._create_test(suite, test_data, defaults)
        for child in data.children:
            suite.suites.append(self._build_suite(child, defaults))
        return suite

 def _parse(self, path):
        try:
            return TestData(source=abspath(path),
                            include_suites=self.include_suites,
                            warn_on_skipped=self.warn_on_skipped)
        except DataError, err:
            raise DataError("Parsing '%s' failed: %s" % (path, unicode(err)))

測試套件suite是什么數據結構? 

 ——TestSuite類的引入方式是“from .model import TestSuite, ForLoop”,也就是目錄為C:\Python27\Lib\site-packages\robot\running\model,

從類TestSuite的代碼來看,它繼承自model.TestCase,路徑是C:\Python27\Lib\site-packages\robot\model\testsuite.py。從以下代碼可知,測試套件suite的數據結構是列表。

TestSuite構造方法,調用了父類的構造方法:

    def __init__(self,  name='', doc='', metadata=None, source=None):

        model.TestSuite.__init__(self, name, doc, metadata, source)
        #: Imports the suite contains.
        self.imports = []
        #: User keywords defined in the same file as the suite.
        #: **Likely to change or to be removed.**
        self.user_keywords = []
        #: Variables defined in the same file as the suite.
        #: **Likely to change or to be removed.**
        self.variables = []

model.TestSuite構造函數

    def __init__(self, name='', doc='', metadata=None, source=None):
        #: Parent :class:`TestSuite` or `None`.
        self.parent = None
        #: Test suite name.
        self.name = name
        #: Test suite documentation.
        self.doc = doc
        #: Test suite metadata as a dictionary.
        self.metadata = metadata
        #: Path to the source file or directory.
        self.source = source
        #: A list of child :class:`~.testsuite.TestSuite` instances.
        self.suites = []
        #: A list of :class:`~.testcase.TestCase` instances.
        self.tests = []
        #: A list containing setup and teardown as
        #: :class:`~keyword.Keyword` instances.
        self.keywords = []
        self._my_visitors = []

 (二)測試套件設置;

調用了C:\Python27\Lib\site-packages\robot\model\testsuite.py文件中TestSuite類的configure方法:

    def configure(self, **options):
        self.visit(SuiteConfigurer(**options))
    def visit(self, visitor):
        visitor.visit_suite(self)

在看下C:\Python27\Lib\site-packages\robot\model\configurer文件中的visit_suite方法,從方法可以看到是設置了測試套件,測試用例,篩選標簽的一些內容。

    def visit_suite(self, suite):
        self._set_suite_attributes(suite)
        self._filter(suite)
        suite.set_tags(self.add_tags, self.remove_tags)

(三)測試套件執行得到執行結果;

 def run(self, settings=None, **options):
        """Executes the suite based based the given ``settings`` or ``options``.

        :param settings: :class:`~robot.conf.settings.RobotSettings` object
            to configure test execution.
        :param options: Used to construct new
            :class:`~robot.conf.settings.RobotSettings` object if ``settings``
            are not given.
        :return: :class:`~robot.result.executionresult.Result` object with
            information about executed suites and tests.

        If ``options`` are used, their names are the same as long command line
        options except without hyphens, and they also have the same semantics.
        Options that can be given on the command line multiple times can be
        passed as lists like ``variable=['VAR1:value1', 'VAR2:value2']``.
        If such an option is used only once, it can be given also as a single
        string like ``variable='VAR:value'``.

        Only options related to the actual test execution have an effect.
        For example, options related to selecting test cases or creating
        logs and reports are silently ignored. The output XML generated
        as part of the execution can be configured, though, including
        disabling it with ``output=None``.

        Example::

            result = suite.run(variable='EXAMPLE:value',
                               critical='regression',
                               output='example.xml',
                               exitonfailure=True,
                               skipteardownonexit=True)
            print result.return_code

        To save memory, the returned
        :class:`~robot.result.executionresult.Result` object object does not
        have any information about the executed keywords. If that information
        is needed, the created output XML file needs to be read  using the
        :class:`~robot.result.resultbuilder.ExecutionResult` factory method.

        See the :mod:`package level <robot.running>` documentation for
        more examples, including how to construct executable test suites and
        how to create logs and reports based on the execution results.
        """
        STOP_SIGNAL_MONITOR.start()
        IMPORTER.reset()
        settings = settings or RobotSettings(options)
        pyloggingconf.initialize(settings['LogLevel'])
        init_global_variables(settings)
        output = Output(settings)
        runner = Runner(output, settings)
        self.visit(runner)
        output.close(runner.result)
        return runner.result

 1、關鍵代碼第一句是runner=Runner(output,settings)根據import內容定位到C:\Python27\Lib\site-packages\robot\running\runner.py,這個Runner類定義了很多執行用例的關鍵方法。

2、關鍵代碼第二句是 self.visit(runner),這和

調用了C:\Python27\Lib\site-packages\robot\model\testsuite.py文件中TestSuite類的visit方法,在這里,入參visitor傳入的是runner對象。

    def visit(self, visitor):
        visitor.visit_suite(self)

Runner類繼承自SuiteVisitor類,從它的父類里找到了visit_suite方法,根據SuiteVisitor的導入路徑定位到C:\Python27\Lib\site-packages\robot\model\visitor.py

class SuiteVisitor(object):

    def visit_suite(self, suite):
        if self.start_suite(suite) is not False:
            suite.keywords.visit(self)
            suite.suites.visit(self)
            suite.tests.visit(self)
            self.end_suite(suite)

 到這里就可以明確,點擊RUN執行用例及之后發生什么事了

1.訪問關鍵字;

2.訪問測試套件;

2.執行用例套件下的測試用例;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM