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\run.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:\n%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:\n%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執行部分復雜,將在下一篇文章中詳細講解


免責聲明!

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



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