Robot Framework 源碼解析(2)- 執(zhí)行測試的入口點

接著上一章肢础,我們先來看?src/robot/run.py?的 run_cli方法仍稀。

def run_cli(arguments=None, exit=True):

? ?if arguments is None:

? ? ? ? arguments = sys.argv[1:]

? ? return RobotFramework().execute_cli(arguments, exit=exit)

方法很簡單左医,只是調用了RobotFramework類中的execute_cli方法节猿。RobotFramework是run.py的一個內部類共耍,也是Application的子類氛改。通過from robot.utils import Application, unic, text可查看Application是做什么的统求。

src/robot/utils/application.py

摘錄部分代碼:

class Application(object):

......

? ? def main(self, arguments, **options):

? ? ? ? raise NotImplementedError

? ....

? ? def execute_cli(self, cli_arguments, exit=True):

? ? ? ? with self._logger:

? ? ? ? ? ? self._logger.info('%s %s' % (self._ap.name, self._ap.version))

? ? ? ? ? ? options, arguments = self._parse_arguments(cli_arguments)

? ? ? ? ? ? rc = self._execute(arguments, options)

? ? ? ? if exit:

? ? ? ? ? ? self._exit(rc)

? ? ? ? return rc

......

? ? def _parse_arguments(self, cli_args):

? ? ? ? try:

? ? ? ? ? ? options, arguments = self.parse_arguments(cli_args)

? ? ? ? except Information as msg:

? ? ? ? ? ? self._report_info(msg.message)

? ? ? ? except DataError as err:

? ? ? ? ? ? self._report_error(err.message, help=True, exit=True)

? ? ? ? else:

? ? ? ? ? ? self._logger.info('Arguments: %s' % ','.join(arguments))

? ? ? ? ? ? return options, arguments

? ? def parse_arguments(self, cli_args):

? ? ? ? """Public interface for parsing command line arguments.

? ? ? ? :param? ? cli_args: Command line arguments as a list

? ? ? ? :returns: options (dict), arguments (list)

? ? ? ? :raises:? :class:`~robot.errors.Information` when --help or --version used

? ? ? ? :raises:? :class:`~robot.errors.DataError` when parsing fails

? ? ? ? """

? ? ? ? return self._ap.parse_args(cli_args)

? ? def execute(self, *arguments, **options):

? ? ? ? with self._logger:

? ? ? ? ? ? self._logger.info('%s %s' % (self._ap.name, self._ap.version))

? ? ? ? ? ? return self._execute(list(arguments), options)

? ? def _execute(self, arguments, options):

? ? ? ? try:

? ? ? ? ? ? rc = self.main(arguments, **options)

? ? ? ?.....

Application的execute_cli方法其實也只是做了參數(shù)的解析工作害淤,具體的任務交給了本實例的main方法。仍然回到?src/robot/run.py?看RobotFramework的main方法:

def main(self, datasources, **options):

? ? ? ? settings = RobotSettings(options)

? ? ? ? LOGGER.register_console_logger(**settings.console_output_config)

? ? ? ? LOGGER.info('Settings:\n%s' % unic(settings))

? ? ? ? builder = TestSuiteBuilder(settings['SuiteNames'],

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? extension=settings.extension,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rpa=settings.rpa)

? ? ? ? suite = builder.build(*datasources)

? ? ? ? settings.rpa = builder.rpa

? ? ? ? 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):

? ? ? ? ? ? old_max_error_lines = text.MAX_ERROR_LINES

? ? ? ? ? ? text.MAX_ERROR_LINES = settings.max_error_lines

? ? ? ? ? ? try:

? ? ? ? ? ? ? ? result = suite.run(settings)

? ? ? ? ? ? finally:

? ? ? ? ? ? ? ? text.MAX_ERROR_LINES = old_max_error_lines

? ? ? ? ? ? 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

在這個方法里卑雁,進行了設置項的賦值募书,并且真正開始了執(zhí)行測試〔舛祝看TestSuiteBuilder是做什么的莹捡。

src/robot/running/builder.py

class TestSuiteBuilder(object):

? ? def __init__(self, include_suites=None, warn_on_skipped='DEPRECATED',

? ? ? ? ? ? ? ? extension=None, rpa=None):

? ? ? ? self.include_suites = include_suites

? ? ? ? self.extensions = self._get_extensions(extension)

? ? ? ? builder = StepBuilder()

? ? ? ? self._build_steps = builder.build_steps

? ? ? ? self._build_step = builder.build_step

? ? ? ? self.rpa = rpa

? ? ? ? self._rpa_not_given = rpa is None

......

? ? def build(self, *paths):

? ? ? ? """

? ? ? ? :param paths: Paths to test data files or directories.

? ? ? ? :return: :class:`~robot.running.model.TestSuite` instance.

? ? ? ? """

? ? ? ? if not paths:

? ? ? ? ? ? raise DataError('One or more source paths required.')

? ? ? ? if len(paths) == 1:

? ? ? ? ? ? return self._parse_and_build(paths[0])

? ? ? ? root = TestSuite()

? ? ? ? for path in paths:

? ? ? ? ? ? root.suites.append(self._parse_and_build(path))

? ? ? ? root.rpa = self.rpa

? ? ? ? return root

? ......

這個TestSuiteBuilder的目的是通過解析datasource來構建一個TestSuite。那TestSuite又是什么的呢扣甲?

從from .model import Keyword, TestCase, TestSuite可知篮赢,TestSuite是在

src/robot/running/model.py

class TestSuite(model.TestSuite):

? ? """Represents a single executable test suite.

? ? See the base class for documentation of attributes not documented here.

? ? """

? ? __slots__ = ['resource']

? ? test_class = TestCase? ? #: Internal usage only.

? ? keyword_class = Keyword? #: Internal usage only.

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

? ? ? ? model.TestSuite.__init__(self, name, doc, metadata, source, rpa)

? ? ? ? #: :class:`ResourceFile` instance containing imports, variables and

? ? ? ? #: keywords the suite owns. When data is parsed from the file system,

? ? ? ? #: this data comes from the same test case file that creates the suite.

? ? ? ? self.resource = ResourceFile(source=source)

? ? def configure(self, randomize_suites=False, randomize_tests=False,

? ? ? ? ? ? ? ? ? randomize_seed=None, **options)

? ? ? ? model.TestSuite.configure(self, **options)

? ? ? ? self.randomize(randomize_suites, randomize_tests, randomize_seed)

? ? def randomize(self, suites=True, tests=True, seed=None):

? ? ? ? """Randomizes the order of suites and/or tests, recursively.

? ? ? ? :param suites: Boolean controlling should suites be randomized.

? ? ? ? :param tests: Boolean controlling should tests be randomized.

? ? ? ? :param seed: Random seed. Can be given if previous random order needs

? ? ? ? ? ? to be re-created. Seed value is always shown in logs and reports.

? ? ? ? """

? ? ? ? self.visit(Randomizer(suites, tests, seed))

? ? def run(self, settings=None, **options):

? ? ? ?.......

? ? ? ? from .namespace import IMPORTER

? ? ? ? from .signalhandler import STOP_SIGNAL_MONITOR

? ? ? ? from .runner import Runner

? ? ? ? with LOGGER:

? ? ? ? ? ? if not settings:

? ? ? ? ? ? ? ? settings = RobotSettings(options)

? ? ? ? ? ? ? ? LOGGER.register_console_logger(**settings.console_output_config)

? ? ? ? ? ? with pyloggingconf.robot_handler_enabled(settings.log_level):

? ? ? ? ? ? ? ? with STOP_SIGNAL_MONITOR:

? ? ? ? ? ? ? ? ? ? IMPORTER.reset()

? ? ? ? ? ? ? ? ? ? output = Output(settings)

? ? ? ? ? ? ? ? ? ? runner = Runner(output, settings)

? ? ? ? ? ? ? ? ? ? self.visit(runner)

? ? ? ? ? ? ? ? output.close(runner.result)

? ? ? ? return runner.result

這里TestSuite是model.TestSuite的子類

/src/robot/model/testsuite.py

def visit(self, visitor):

? ? ? ? """:mod:`Visitor interface <robot.model.visitor>` entry-point."""

? ? ? ? visitor.visit_suite(self)

這里只是調用了Runner的visit_suite方法,來看一下

src/robot/running/runner.py

class Runner(SuiteVisitor):

Runner只是SuiteVisitor的一個子類琉挖,看看SuiteVisitor

/src/robot/model/visitor.py

class SuiteVisitor(object):

? ? """Abstract class to ease traversing through the test suite structure.

? ? See the :mod:`module level <robot.model.visitor>` documentation for more

? ? information and an example.

? ? """

? ? def visit_suite(self, suite):

? ? ? ? """Implements traversing through the suite and its direct children.

? ? ? ? Can be overridden to allow modifying the passed in ``suite`` without

? ? ? ? calling :func:`start_suite` or :func:`end_suite` nor visiting child

? ? ? ? suites, tests or keywords (setup and teardown) at all.

? ? ? ? """

? ? ? ? if self.start_suite(suite) is not False:

? ? ? ? ? ? suite.keywords.visit(self)

? ? ? ? ? ? suite.suites.visit(self)

? ? ? ? ? ? suite.tests.visit(self)

? ? ? ? ? ? self.end_suite(suite)

? .......

start_suite / end_suite 就是在Runner具體實現(xiàn)的启泣。

具體看start_suite是做什么的:

def start_suite(self, suite):

? ? ? ? self._output.library_listeners.new_suite_scope()

? ? ? ? result = TestSuite(source=suite.source,

? ? ? ? ? ? ? ? ? ? ? ? ? name=suite.name,

? ? ? ? ? ? ? ? ? ? ? ? ? doc=suite.doc,

? ? ? ? ? ? ? ? ? ? ? ? ? metadata=suite.metadata,

? ? ? ? ? ? ? ? ? ? ? ? ? starttime=get_timestamp(),

? ? ? ? ? ? ? ? ? ? ? ? ? rpa=self._settings.rpa)

? ? ? ? if not self.result:

? ? ? ? ? ? result.set_criticality(self._settings.critical_tags,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._settings.non_critical_tags)

? ? ? ? ? ? self.result = Result(root_suite=result, rpa=self._settings.rpa)

? ? ? ? ? ? self.result.configure(status_rc=self._settings.status_rc,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? stat_config=self._settings.statistics_config)

? ? ? ? else:

? ? ? ? ? ? self._suite.suites.append(result)

? ? ? ? self._suite = result

? ? ? ? self._suite_status = SuiteStatus(self._suite_status,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._settings.exit_on_failure,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._settings.exit_on_error,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._settings.skip_teardown_on_exit)

? ? ? ? ns = Namespace(self._variables, result, suite.resource)

? ? ? ? ns.start_suite()

? ? ? ? ns.variables.set_from_variable_table(suite.resource.variables)

? ? ? ? EXECUTION_CONTEXTS.start_suite(result, ns, self._output,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._settings.dry_run)

? ? ? ? self._context.set_suite_variables(result)

? ? ? ? if not self._suite_status.failures:

? ? ? ? ? ? ns.handle_imports()

? ? ? ? ? ? ns.variables.resolve_delayed()

? ? ? ? result.doc = self._resolve_setting(result.doc)

? ? ? ? result.metadata = [(self._resolve_setting(n), self._resolve_setting(v))

? ? ? ? ? ? ? ? ? ? ? ? ? for n, v in result.metadata.items()]

? ? ? ? self._context.set_suite_variables(result)

? ? ? ? self._output.start_suite(ModelCombiner(suite, result,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tests=suite.tests,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? suites=suite.suites,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? test_count=suite.test_count))

? ? ? ? self._output.register_error_listener(self._suite_status.error_occurred)

? ? ? ? self._run_setup(suite.keywords.setup, self._suite_status)

? ? ? ? self._executed_tests = NormalizedDict(ignore='_')

def end_suite(self, suite):

? ? ? ? self._suite.message = self._suite_status.message

? ? ? ? self._context.report_suite_status(self._suite.status,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self._suite.full_message)

? ? ? ? with self._context.suite_teardown():

? ? ? ? ? ? failure = self._run_teardown(suite.keywords.teardown, self._suite_status)

? ? ? ? ? ? if failure:

? ? ? ? ? ? ? ? self._suite.suite_teardown_failed(unic(failure))

? ? ? ? ? ? ? ? if self._suite.statistics.critical.failed:

? ? ? ? ? ? ? ? ? ? self._suite_status.critical_failure_occurred()

? ? ? ? self._suite.endtime = get_timestamp()

? ? ? ? self._suite.message = self._suite_status.message

? ? ? ? self._context.end_suite(ModelCombiner(suite, self._suite))

? ? ? ? self._suite = self._suite.parent

? ? ? ? self._suite_status = self._suite_status.parent

? ? ? ? self._output.library_listeners.discard_suite_scope()

執(zhí)行到這里,會根據(jù)設置和datasource 已經開始了收集測試結果示辈。

回到最初的?src/robot/run.py

根據(jù)

if __name__ == '__main__':

? ? run_cli(sys.argv[1:])

可以看出寥茫,run.py不僅可以通過java -jar robotframework.jar?run?mytests.robot,被最終調用矾麻,還可以直接使用命令纱耻,例如:

? ? ? ? robot path/to/tests.robot

? ? ? ? robot --include tag1 --include tag2 --splitlog tests.robot

? ? ? ? robot --name Example --log NONE t1.robot t2.robot > stdout.txt

來執(zhí)行robotframework的測試用例。

如果喜歡作者的文章险耀,請關注"寫代碼的猿"訂閱號以便第一時間獲得最新內容弄喘。本文版權歸作者所有,歡迎轉載.?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末甩牺,一起剝皮案震驚了整個濱河市蘑志,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贬派,老刑警劉巖急但,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赠群,居然都是意外死亡羊始,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門查描,熙熙樓的掌柜王于貴愁眉苦臉地迎上來突委,“玉大人,你說我怎么就攤上這事冬三≡扔停” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵勾笆,是天一觀的道長敌蚜。 經常有香客問我,道長窝爪,這世上最難降的妖魔是什么弛车? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任齐媒,我火速辦了婚禮,結果婚禮上纷跛,老公的妹妹穿的比我還像新娘喻括。我一直安慰自己,他們只是感情好贫奠,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布唬血。 她就那樣靜靜地躺著,像睡著了一般唤崭。 火紅的嫁衣襯著肌膚如雪拷恨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天谢肾,我揣著相機與錄音腕侄,去河邊找鬼。 笑死勒叠,一個胖子當著我的面吹牛兜挨,可吹牛的內容都是我干的。 我是一名探鬼主播眯分,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼拌汇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弊决?” 一聲冷哼從身側響起噪舀,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎飘诗,沒想到半個月后与倡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡昆稿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年纺座,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溉潭。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡净响,死狀恐怖,靈堂內的尸體忽然破棺而出喳瓣,到底是詐尸還是另有隱情馋贤,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布畏陕,位于F島的核電站配乓,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜犹芹,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一崎页、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧羽莺,春花似錦实昨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丈挟。三九已至刁卜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曙咽,已是汗流浹背蛔趴。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留例朱,地道東北人孝情。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像洒嗤,于是被迫代替她去往敵國和親箫荡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • 因為unittest支持的html報告在作為郵件附加時耗時較長渔隶,故將報告擴展支持為unishark框架羔挡。 基于un...
    五娃兒閱讀 524評論 0 0
  • Robotframwork 自帶的重試參數(shù)-R Note:out.html路徑,同時要保證有l(wèi)og.html文件和...
    五娃兒閱讀 1,757評論 1 1
  • 準備開賽啦间唉!準備開賽啦绞灼!還有不到一個月的時間,2016-2017賽季NBA常規(guī)賽即將開打啦呈野〉桶快點大聲告訴我,你是哪...
    產品運營運營閱讀 394評論 0 1
  • 簡單版 給自己設立3條做事的準則 一被冒、個人 1军掂、注重做事情的細節(jié) 2、做好提前準備工作 3姆打、不斷的學習良姆,提升自我 ...
    鴿子蛋m閱讀 136評論 0 0
  • 孩子剛剛滿月,便不給我衣服幔戏,說要回去玛追,手疼,回去休息休息,最后我說孩子大點要去天津買房痊剖,這件事情罷休韩玩。還有一次老頭...
    紫荊花花閱讀 723評論 0 0