zoukankan      html  css  js  c++  java
  • RobotFramework学习系列(一)

      测试工作需要用到了RobotFramework,之前只是使用,用了一段时间后,决定深入研究一下它。使用平台为Windows + Linux。由于是基于Linux 系统方面的测试工作,使用的RobotFramework版本为2.8.5

      先简单介绍一下Robot中的一些概念

      关键字(Keyword): 分为内部关键字和用户关键字。

          内部关键字顾名思义为Robot内置的部分关键字,例如Should Be Equal,Run Keyword 等等

          用户关键字,开发人员或者测试人员自行通过内部关键字组合 OR 采用外部代码导入构成的关键字

      资源(Resource):  关键字的组合包,例如将多个关键字放在一个文件里,这个文件在Robot里可以称为一个资源

      库(Library): Robot支持很多库,分为内置和外置,内置的如Collections等,外置的,可以是python写成的文件,java写成的文件,以及通过xml-rpc导入的远程库,这些库一般都是通过代码写成

      用例(Case):多个关键字的组合,顺序 or 带分支控制等组成的文件为Case

      Suite:可以理解为多个用例的组合,一个suit 文件下可以写多条用例。

      Windows的入口文件为pybot.bat

      

       Linux的入口文件为pybot

      

       由上图可以知道,robot的入口文件为robot un.py(可以查看robot\__init__.py中代码可知)

      看一下run_cli()和run()的区别

      

       从上图和下图的代码看,run_cli调用的是RobotFramework类下的execute_cli方法,run调用的是RobotFramework类下的execute方法,不同的是execute_cli多了一些参数解析的操作,所以推荐当代码中调用的时候,使用run_cli,而当工具调用的时候,使用run。

      不管是run还是run_cli,核心都调用了_execute这个内部函数,而它又调用了main函数,这个main,在Application类中是一个需要被继承重写的函数,所以需要去继承它的类中寻找具体实现

     1     def execute_cli(self, cli_arguments):
     2         with self._logging():
     3             options, arguments = self._parse_arguments(cli_arguments)
     4             rc = self._execute(arguments, options)
     5         self._exit(rc)
     6 
     7     def execute(self, *arguments, **options):
     8         with self._logging():
     9             return self._execute(list(arguments), options)
    10 
    11     def _execute(self, arguments, options):
    12         try:
    13             rc = self.main(arguments, **options)
    14         except DataError, err:
    15             return self._report_error(unicode(err), help=True)
    16         except (KeyboardInterrupt, SystemExit):
    17             return self._report_error('Execution stopped by user.',
    18                                       rc=STOPPED_BY_USER)
    19         except:
    20             error, details = get_error_details()
    21             return self._report_error('Unexpected error: %s' % error,
    22                                       details, rc=FRAMEWORK_ERROR)
    23         else:
    24             return rc or 0

      下面为main函数的具体实现,核心为5~9行,5行前面为初始化一些基础的配置,9行以后为生成测试log

     1     def main(self, datasources, **options):
     2         settings = RobotSettings(options)
     3         LOGGER.register_console_logger(**settings.console_logger_config)
     4         LOGGER.info('Settings:
    %s' % unicode(settings))
     5         suite = TestSuiteBuilder(settings['SuiteNames'],
     6                                  settings['WarnOnSkipped'],
     7                                  settings['RunEmptySuite']).build(*datasources)
     8         suite.configure(**settings.suite_config)
     9         result = suite.run(settings)
    10         LOGGER.info("Tests execution ended. Statistics:
    %s"
    11                     % result.suite.stat_message)
    12         if settings.log or settings.report or settings.xunit:
    13             writer = ResultWriter(settings.output if settings.log else result)
    14             writer.write_results(settings.get_rebot_settings())
    15         return result.return_code

      以windows运行命令行为例  python run.py --outputdir  C:\logs  --test 初始化环境   C:\环境验证

      5~7行,suite为TestSuiteBuilder根据Settings中的部分参数进行构建,并执行build函数,其中datasources为List类型的数据,即['C:\环境验证'],说明指定用例suit的时候,是可以同时指定多个Suit一起跑

      9行,为执行指定suite(用例),并获取结果

      看一下build具体执行操作

     1     def build(self, *paths):
     2         if not paths:
     3             raise DataError('One or more source paths required.')
     4         if len(paths) == 1:
     5             return self._build_and_check_if_empty(paths[0])
     6         root = TestSuite()
     7         for path in paths:
     8             root.suites.append(self._build_and_check_if_empty(path))
     9         return root
    10     def _build_and_check_if_empty(self, path):
    11         builded = self._build_suite(self._parse(path))
    12         if not self._empty_suites_allowed and not builded.test_count:
    13                 raise DataError("Suite '%s' contains no tests." % builded.name)
    14         builded.remove_empty_suites()
    15         return builded
    16 
    17    def _build_suite(self, data, parent_defaults=None):
    18         defaults = TestDefaults(data.setting_table, parent_defaults)
    19         suite = TestSuite(name=data.name,
    20                           source=data.source,
    21                           doc=unicode(data.setting_table.doc),
    22                           metadata=self._get_metadata(data.setting_table))
    23         for import_data in data.setting_table.imports:
    24             self._create_import(suite, import_data)
    25         self._create_setup(suite, data.setting_table.suite_setup)
    26         self._create_teardown(suite, data.setting_table.suite_teardown)
    27         for var_data in data.variable_table.variables:
    28             self._create_variable(suite, var_data)
    29         for uk_data in data.keyword_table.keywords:
    30             self._create_user_keyword(suite, uk_data)
    31         for test_data in data.testcase_table.tests:
    32             self._create_test(suite, test_data, defaults)
    33         for child in data.children:
    34             suite.suites.append(self._build_suite(child, defaults))
    35         return suite

      build根据传入的datasources的个数进行构建,对于单独的一个文件的情况下,调用了内部函数_build_and_check_if_empty,这个函数会判断传入的datasource是否真实存在,如果存在,会再次调用_build_suite进行suite构建

      _parse()这个内部的函数完成了robot文件(.txt 或者.robot文件)到robot的数据结构TestData的转换, TestData数据结构会在后面的文章详细讲解

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

      回到_build_suite

      data.setting_table.imports是指suite最开始定义的可以进行导入的Source, Libriary等

      data.setting_table.suite_setup 是指在suite中定义的  Suite Setup,同理  data.setting_table.suite_teardown 是指 Suite Teardown 

      data.variable_table.variables 是指在suite中定义的变量

      data.keyword_table.keywords 定义的关键字

      data.testcase_table.tests 是指suite中定义的case,对test,会调用下面的代码进行创建test

     1     def _create_test(self, suite, data, defaults):
     2         values = defaults.get_test_values(data)
     3         test = suite.tests.create(name=data.name,
     4                                   doc=unicode(data.doc),
     5                                   tags=values.tags.value,
     6                                   template=self._get_template(values.template),
     7                                   timeout=self._get_timeout(values.timeout))
     8         self._create_setup(test, values.setup)
     9         for step_data in data.steps:
    10             self._create_step(test, step_data, template=values.template)
    11         self._create_teardown(test, values.teardown)

      values是获取了单个test的一些具体信息,如tags,test Setup, Test Teardown等

      suite.tests为 TestCase类型数据,TestSuite 继承于model.TestSuite, 而model.TestSuite 中通过装饰器为tests 赋予TestCase类型数据

    1 @setter
    2     def tests(self, tests):
    3         return TestCases(self.test_class, self, tests)

      后续代码创建了分解了整个case的各个步骤,通过_create_step对test的步骤进行扩充

      到此为止,TestSuiteBuilder便构建完成

      由于Case执行部分复杂,将在下一篇文章中详细讲解

  • 相关阅读:
    2011年上半年软考信息系统项目管理师顺利通过了。
    技术工程师之歌
    大家都很注重能力的时候,我却在努力提高学历
    新的开发团队配合模式,适合小型团队
    研发部的四套马车
    mysql给root开启远程访问权限,修改root密码
    js客户端判断文件大小限制上传
    metro 微博api开发,post请求
    VS2010在C#头文件添加文件注释的方法
    android ListView 常见问题 之 高度问题
  • 原文地址:https://www.cnblogs.com/sumoning/p/12511407.html
Copyright © 2011-2022 走看看