def pytest_fixture_setup(self, fixturedef, request): fixture_name = fixturedef.argname container_uuid = self._cache.get(fixturedef) if not container_uuid: container_uuid = self._cache.set(fixturedef) container = TestResultContainer(uuid=container_uuid) self.allure_logger.start_group(container_uuid, container) self.allure_logger.update_group(container_uuid, start=now()) before_fixture_uuid = uuid4 before_fixture = TestBeforeResult(name=fixture_name, start=now()) self.allure_logger.start_before_fixture(container_uuid, before_fixture_uuid, before_fixture) parameters = allure_parameters(fixturedef, request) if parameters: test_uuid = self._cache.get(request._pyfuncitem.nodeid) parameters = Parameter(**parameters) if parameters else [] self.allure_logger.update_test(test_uuid, parameters=parameters) yield self.allure_logger.stop_before_fixture(before_fixture_uuid, stop=now()) for index, finalizer in enumerate(fixturedef._finalizer or ()): fixturedef._finalizer[index] = FinalizerSpy( container_uuid, fixturedef.argname, finalizer, self.config)
def start_keyword(self, name, attributes, is_message=False): # logger.console('\nstart_keyword: ['+name+']') # logger.console(' ['+attributes['type']+'] [stack lenght] ['+str(len(self.stack))+'] [testsuite lenght] ['+ str(len(self.testsuite.tests))+']') if (not is_message and hasattr(self, attributes.get('kwname').replace(" ", "_")) and callable( getattr(self, attributes.get('kwname').replace(" ", "_")))): libraryMethodToCall = getattr( self, attributes.get('kwname').replace(" ", "_")) result = libraryMethodToCall(name, attributes) keyword = TestStep( name=name, title=attributes.get('kwname'), attachments=[], steps=[], start=now(), ) if self.stack: self.stack.append(keyword) return keyword if (attributes.get('type') == 'Keyword' or (attributes.get('type') == 'Teardown' and len(self.stack) is not 0)): keyword = TestStep( name=name, title=attributes.get('kwname'), attachments=[], steps=[], start=now(), ) if self.stack: self.stack.append(keyword) return keyword """ Processing the Suite Setup. Although there is no test case yet, a virtual one is created to allow for the inclusion of the keyword. """ if (attributes.get('type') == 'Setup'): self.start_suitesetup(name, attributes) return if (attributes.get('type') == 'Teardown' and len(self.stack) == 0): self.start_suitesetup(name, attributes) return
def log_message(self, msg): # logger.console(pprint.pformat(msg)) # logger.console(self.stack[-1].title) # Check to see if there are any items to add the log message to # this check is needed because otherwise Suite Setup may fail. if len(self.stack) > 0: if self.stack[-1].title == 'Capture Page Screenshot': screenshot = re.search('[a-z]+-[a-z]+-[0-9]+.png', msg['message']) if screenshot: self.attach('{}'.format(screenshot.group(0)), screenshot.group(0)) if (msg['html'] == 'yes'): screenshot = re.search('[a-z]+-[a-z]+-[0-9]+.png', msg['message']) if screenshot: kwname = '{}'.format(screenshot.group(0)) else: kwname = msg['message'] # logger.console('kwname: '+kwname) else: kwname = msg['message'] startKeywordArgs = { 'args': [], 'assign': [], 'doc': '', 'kwname': kwname, 'libname': 'BuiltIn', 'starttime': now(), 'tags': [], 'type': 'Keyword' } self.start_keyword('Log Message', startKeywordArgs, True) endKeywordArgs = { 'args': [], 'assign': [], 'doc': '', 'elapsedtime': 0, 'endtime': now(), 'kwname': kwname, 'libname': 'BuiltIn', 'starttime': now(), 'status': 'PASS', 'tags': [], 'type': 'Keyword' } self.end_keyword('Log Message', endKeywordArgs) return
def end_keyword(self, name, attributes): # logger.console('\nend_keyword: ['+name+']') # logger.console(' ['+attributes['type']+'] [stack lenght] ['+str(len(self.stack))+'] [testsuite lenght] ['+ str(len(self.testsuite.tests))+']') if len(self.stack) > 0: if (attributes.get('type') == 'Keyword' or (attributes.get('type') == 'Teardown' and isinstance(self.stack[-1], TestStep) is True)): step = self.stack.pop() if (attributes.get('status') == 'FAIL'): step.status = 'failed' elif (attributes.get('status') == 'PASS'): step.status = 'passed' step.stop = now() # Append the step to the previous item. This can be another step, or # another keyword. self.stack[-1].steps.append(step) return if (attributes.get('type') == 'Setup'): self.end_suitesetup(name, attributes) return if (attributes.get('type') == 'Teardown'): self.end_suitesetup(name, attributes) return return
def start_suitesetup(self, name, attributes): start_test_attributes = { 'critical': 'yes', 'doc': 'Test Suite Setup section', 'starttime': attributes['starttime'], 'tags': [], 'id': 's1-s1-t0', 'longname': BuiltIn().get_variable_value('${SUITE_NAME}'), 'template': '' } if len(str(start_test_attributes.get('doc'))) > 0: description = str(start_test_attributes.get('doc')) else: description = name test = TestCase( name=name, description=description, start=now(), attachments=[], labels=[], # parameters=[], steps=[]) self.stack.append(test) return
def pytest_runtest_protocol(self, item, nextitem): try: # for common items description = item.function.__doc__ except AttributeError: # for doctests that has no `function` attribute description = item.reportinfo()[2] self.test = TestCase( name='.'.join( mangle_testnames( [x.name for x in parent_down_from_module(item)])), description=description, start=now(), attachments=[], labels=labels_of(item), status=None, steps=[], id=str(uuid.uuid4()) ) # for later resolution in AllureAgregatingListener.pytest_sessionfinish self.stack = [self.test] yield self.test = None self.stack = []
def end_suitesetup(self, name, attributes): step = self.stack.pop() if attributes.get('status') == Robot.PASS: step.status = Status.PASSED else: step.status = Status.FAILED step.description = attributes.get('doc') step.stop = now() step = TestStep(name=name, title=attributes.get('kwname'), attachments=[], status=step.status, steps=step.steps, start=step.start, stop=step.stop) if attributes.get('type') == 'Setup': if not self.stack: self.suite_setup = step else: self.stack[-1].steps.append(step) else: self.suite_teardown = step #self.testsuite.tests.append(test) return step
def _fill_case(self, report, call, pyteststatus, status): """ Finalizes with important data :param report: py.test's `TestReport` :param call: py.test's `CallInfo` :param pyteststatus: the failed/xfailed/xpassed thing :param status: a :py:class:`allure.constants.Status` entry """ [ self.attach(name, contents, AttachmentType.TEXT) for (name, contents) in dict(report.sections).items() ] self.test.stop = now() self.test.status = status if status in FAILED_STATUSES: self.test.failure = Failure( message=get_exception_message(call.excinfo, pyteststatus, report), trace=report.longrepr or hasattr(report, 'wasxfail') and report.wasxfail) elif status in SKIPPED_STATUSES: skip_message = type( report.longrepr ) == tuple and report.longrepr[2] or report.wasxfail trim_msg_len = 89 short_message = skip_message.split('\n')[0][:trim_msg_len] # FIXME: see pytest.runner.pytest_runtest_makereport self.test.failure = Failure( message=(short_message + '...' * (len(skip_message) > trim_msg_len)), trace=status == Status.PENDING and report.longrepr or short_message != skip_message and skip_message or '')
def _fill_case(self, report, call, pyteststatus, status): """ Finalizes with important data :param report: py.test's `TestReport` :param call: py.test's `CallInfo` :param pyteststatus: the failed/xfailed/xpassed thing :param status: a :py:class:`allure.constants.Status` entry """ [self.attach(name, contents, AttachmentType.TEXT) for (name, contents) in dict(report.sections).items()] self.test.stop = now() self.test.status = status if status in FAILED_STATUSES: self.test.failure = Failure( message=get_exception_message(call.excinfo, pyteststatus, report), trace=report.longrepr or hasattr(report, "wasxfail") and report.wasxfail, ) elif status in SKIPPED_STATUSES: skip_message = type(report.longrepr) == tuple and report.longrepr[2] or report.wasxfail trim_msg_len = 89 short_message = skip_message.split("\n")[0][:trim_msg_len] # FIXME: see pytest.runner.pytest_runtest_makereport self.test.failure = Failure( message=(short_message + "..." * (len(skip_message) > trim_msg_len)), trace=status == Status.PENDING and report.longrepr or short_message != skip_message and skip_message or "", )
def stop_scenario(self, scenario): status = scenario_status(scenario) status_details = scenario_status_details(scenario) self.logger.update_test(self.current_scenario_uuid, stop=now(), status=status, statusDetails=status_details) self.logger.close_test(self.current_scenario_uuid) self.current_scenario_uuid = None self.current_step_uuid = None
def stop_suite(self): """ Stops current test suite and writes it to the file in the report directory """ self.testsuite.stop = now() with self._reportfile('%s-testsuite.xml' % uuid.uuid4()) as f: self._write_suite(f, self.testsuite)
def start_suite(self, name, description=None, title=None): """ Starts a new Suite with given ``name`` and ``description`` """ self.testsuite = TestSuite(name=name, title=title, description=description, tests=[], start=now())
def pytest_allure_after_step(self, uuid, exc_type, exc_val, exc_tb): status = Status.PASSED if exc_type is not None: if exc_type == pytest.skip.Exception: status = Status.SKIPPED else: status = Status.FAILED self.allure_logger.stop_step(uuid, stop=now(), status=status)
def start_step(self, name): """ Starts an new :py:class:`allure.structure.TestStep` with given ``name``, pushes it to the ``self.stack`` and returns the step. """ step = TestStep(name=name, title=name, start=now(), attachments=[], steps=[]) self.stack[-1].steps.append(step) self.stack.append(step) return step
def start_step(self, name): """ Starts an new :py:class:`allure.structure.TestStep` with given ``name``, pushes it to the ``self.stack`` and returns the step. """ step = TestStep(name=name, start=now(), attachments=[], steps=[]) self.stack[-1].steps.append(step) self.stack.append(step) return step
def start_suite(self, name, attributes): self.SuitSrc = BuiltIn().get_variable_value('${SUITE_SOURCE}') self.issuetracker = BuiltIn().get_variable_value('${ISSUE_TRACKER}') self.logdir = BuiltIn().get_variable_value('${OUTPUT_DIR}') self.RobotLogDir = BuiltIn().get_variable_value('${ALLURE_OUTPUT_DIR}') # Reading the Allure Properties file for the Issue Id regular expression # for the Issues and the URL to where the Issues/Test Man links should go. seperator = "=" self.AllurePropPath = self.SuitSrc + '\\allure.properties' if os.path.exists(self.AllurePropPath) is True: with open(self.SuitSrc+'\\allure.properties') as f: for line in f: if seperator in line: name, value = line.split(seperator, 1) self.AllureProperties.append({name.strip() : value.strip()}) if name.strip() == 'allure.issues.id.pattern': self.AllureIssueIdRegEx = value.strip() # Setting this variable prevents the loading of a Library added Listener. # I case the Listener is added via Command Line, the Robot Context is not # yet there and will cause an exceptions. Similar section in __init__. ListenerList = BuiltIn().get_variable_value('${AllureListenerActive}', False) BuiltIn().set_global_variable('${AllureListenerActive}', True) # When running a Robot folder, the folder itself is also considered a Suite # The full check depends on the availability of all the vars which are # only available when a Robot file has started. IsSuiteDirectory = os.path.isdir(self.SuitSrc) if(not(IsSuiteDirectory)): ''' Check if class received Output Directory Path through initialisation. ''' if self.allurelogdir is None: '''' Check if in the Robot file the variable has been set.''' if self.RobotLogDir is not None: self.allurelogdir = self.RobotLogDir else: ''' No Path was provided, so using output dir with additional sub folder. ''' self.allurelogdir = BuiltIn().get_variable_value('${OUTPUT_DIR}') + "\\Allure" self.AllureImplc = AllureImpl(self.allurelogdir) if attributes.get('doc') is not '': description = attributes.get('doc') else: description = name self.testsuite = TestSuite(name=name, title=name, description=description, tests=[], labels=[], start=now()) return
def start_keyword(self, name, attributes): if(attributes.get('type') == 'Keyword'): keyword = TestStep(name=name, title=name, attachments=[], steps=[], start=now(),) if self.stack: # self.stack[-1].steps.append(keyword) self.stack.append(keyword) return keyword
def start_case(self, name, description=None, labels=None): """ Starts a new :py:class:`allure.structure.TestCase` """ test = TestCase(name=name, description=description, start=now(), attachments=[], labels=labels or [], steps=[]) self.stack.append(test)
def end_suitesetup(self, name, attributes): end_test_attributes = { 'critical': 'yes', 'doc': 'Test Suite Setup section', 'starttime': attributes['starttime'], 'endtime': attributes['endtime'], 'status': 'PASS', 'tags': [], 'id': 's1-s1-t0', 'longname': BuiltIn().get_variable_value('${SUITE_NAME}'), 'template': '' } test = self.stack.pop() BuiltIn().run_keyword(name) if end_test_attributes.get('status') == Robot.PASS: test.status = Status.PASSED elif end_test_attributes.get('status') == Robot.FAIL: test.status = Status.FAILED test.failure = Failure(message=end_test_attributes.get('message'), trace='') elif end_test_attributes.get('doc') is not '': test.description = attributes.get('doc') if end_test_attributes['tags']: for tag in end_test_attributes['tags']: if re.search(self.AllureIssueIdRegEx, tag): test.labels.append(TestLabel(name=Label.ISSUE, value=tag)) if tag.startswith('feature'): test.labels.append( TestLabel(name='feature', value=tag.split(':')[-1])) if tag.startswith('story'): test.labels.append( TestLabel(name='story', value=tag.split(':')[-1])) elif tag in SEVERITIES: test.labels.append(TestLabel(name='severity', value=tag)) elif tag in STATUSSES: test.status = tag # overwrites the actual test status with this value. self.PabotPoolId = BuiltIn().get_variable_value( '${PABOTEXECUTIONPOOLID}') if (self.PabotPoolId is not None): self.threadId = 'PabotPoolId-' + str(self.PabotPoolId) else: self.threadId = threading._get_ident() test.labels.append(TestLabel(name='thread', value=str(self.threadId))) self.testsuite.tests.append(test) test.stop = now() return test
def start_case(self, name, description=None, severity=Severity.NORMAL): """ Starts a new :py:class:`allure.structure.TestCase` """ test = TestCase(name=name, description=description, severity=severity, start=now(), attachments=[], steps=[]) self.stack.append(test)
def end_test(self, name, attributes): # logger.console('\nend_test: ['+name+']') # logger.console(attributes) # logger.console(' [stack lenght] ['+str(len(self.stack))+'] [testsuite lenght] ['+ str(len(self.testsuite.tests))+']') test = self.stack.pop() if attributes.get('status') == Robot.PASS: test.status = Status.PASSED elif attributes.get('status')==Robot.FAIL: test.status = Status.FAILED test.failure = Failure(message=attributes.get('message'), trace='') elif attributes.get('doc') is not '': test.description = attributes.get('doc') if attributes['tags']: for tag in attributes['tags']: if re.search(self.AllureIssueIdRegEx, tag): test.labels.append(TestLabel( name=Label.ISSUE, value=tag)) elif tag.startswith('feature'): test.labels.append(TestLabel( name='feature', value=tag.split(':')[-1])) elif tag.startswith('story'): test.labels.append(TestLabel( name='story', value=tag.split(':')[-1])) elif tag in SEVERITIES: test.labels.append(TestLabel( name='severity', value=tag)) test.severity = tag elif tag in STATUSSES: test.status = tag # overwrites the actual test status with this value. else: test.labels.append(TestLabel( name='tag', value=tag)) self.PabotPoolId = BuiltIn().get_variable_value('${PABOTEXECUTIONPOOLID}') if(self.PabotPoolId is not None): self.threadId = 'PabotPoolId-' + str(self.PabotPoolId) else: self.threadId = threading._get_ident() test.labels.append(TestLabel( name='thread', value=str(self.threadId))) self.testsuite.tests.append(test) test.stop = now() return test
def start_suitesetup(self, name, attributes): severity = 'blocker' test = TestCase(name=name, description=name, start=now(), attachments=[], labels=[], severity=severity, steps=[]) self.stack.append(test) return
def end_suite(self, name, attributes): self.testsuite.stop = now() logfilename = '%s-testsuite.xml' % uuid.uuid4() # When running a folder, the folder itself is also considered a Suite # The full check depends on the availability of all the vars which are # only available when a Robot file has started. IsSuiteDirectory = os.path.isdir(BuiltIn().get_variable_value("${SUITE_SOURCE}")) if(not(IsSuiteDirectory)): with self.AllureImplc._reportfile(logfilename) as f: self.AllureImplc._write_xml(f, self.testsuite) return
def start_step(self, step): self.current_step_uuid = uuid4() name = u'{keyword} {title}'.format(keyword=step.keyword, title=step.name) parent_uuid = self.current_before_uuid or self.current_scenario_uuid allure_step = TestStepResult(name=name, start=now()) self.logger.start_step(parent_uuid, self.current_step_uuid, allure_step) if step.text: self.logger.attach_data(uuid4(), step.text, name='.text', attachment_type=AttachmentType.TEXT) if step.table: table = [','.join(step.table.headings)] [table.append(','.join(list(row))) for row in step.table.rows] self.logger.attach_data(uuid4(), '\n'.join(table), name='.table', attachment_type=AttachmentType.CSV)
def start_test(self, name, attributes): if len(str(attributes.get('doc'))) > 0: description = str(attributes.get('doc')) else: description = name test = TestCase(name=name, description=description, start=now(), attachments=[], labels=[], # parameters=[], steps=[]) self.stack.append(test) return
def pytest_runtest_protocol(self, item, nextitem): self.test = TestCase(name='.'.join(mangle_testnames([x.name for x in parent_down_from_module(item)])), description=item.function.__doc__, start=now(), attachments=[], labels=labels_of(item), status=None, steps=[], id=str(uuid.uuid4())) # for later resolution in AllureAgregatingListener.pytest_sessionfinish self.stack = [self.test] yield self.test = None self.stack = []
def start_test(self, name, attributes): if len(attributes.get('doc')) > 0: description = attributes.get('doc') else: description = name severity = 'critical' if attributes['critical'] == 'yes' else 'normal' test = TestCase(name=name, description=description, start=now(), attachments=[], severity=severity, labels=[], steps=[]) self.stack.append(test) return
def stop_case(self, status, message=None, trace=None): """ :arg status: one of :py:class:`allure.constants.Status` :arg message: error message from the test :arg trace: error trace from the test Finalizes with important data the test at the top of ``self.stack`` and returns it If either ``message`` or ``trace`` are given adds a ``Failure`` object to the test with them. """ test = self.stack[-1] test.status = status test.stop = now() if message or trace: test.failure = Failure(message=message, trace=trace) self.testsuite.tests.append(test) return test
def stop_case(self, status, message=None, trace=None): """ :arg status: one of :py:class:`allure.constants.Status` :arg message: error message from the test :arg trace: error trace from the test Finalizes with important data the test at the top of ``self.stack`` and returns it If either ``message`` or ``trace`` are given adds a ``Failure`` object to the test with them. """ test = self.stack[-1] test.status = status test.stop = now() if message or trace: test.failure = Failure(message=message, trace=trace or '') self.testsuite.tests.append(test) return test
def start_scenario(self, scenario): self.current_scenario_uuid = uuid4() test_case = TestResult(uuid=self.current_scenario_uuid, start=now()) test_case.name = scenario_name(scenario) test_case.historyId = scenario_history_id(scenario) test_case.description = '\n'.join(scenario.description) labels = [] feature_label = Label(name=LabelType.FEATURE.value, value=scenario.feature.name) severity = (Label(name=LabelType.SEVERITY.value, value=scenario_severity(scenario).value)) labels.append(feature_label) labels.append(severity) labels += [Label(name=LabelType.TAG.value, value=tag) for tag in scenario_tags(scenario)] test_case.parameters = scenario_parameters(scenario) test_case.labels = labels self.logger.schedule_test(self.current_scenario_uuid, test_case) self.update_group()
def pytest_runtest_protocol(self, item, nextitem): try: # for common items description = item.function.__doc__ except AttributeError: # for doctests that has no `function` attribute description = item.reportinfo()[2] self.test = TestCase(name='.'.join(mangle_testnames([x.name for x in parent_down_from_module(item)])), description=description, start=now(), attachments=[], labels=labels_of(item), status=None, steps=[], id=str(uuid.uuid4())) # for later resolution in AllureAgregatingListener.pytest_sessionfinish self.stack = [self.test] yield self.test = None self.stack = []
def _fill_case(self, report, call, pyteststatus, status): """ Finalizes with important data :param report: py.test's `TestReport` :param call: py.test's `CallInfo` :param pyteststatus: the failed/xfailed/xpassed thing :param status: a :py:class:`allure.constants.Status` entry """ # To enable color coding in stdout log we have to write allure attachments as html. for (name, contents) in dict(report.sections).items(): if "stdout" in name: attachment_type = AttachmentType.HTML formatted_contents = self._convert_to_html(contents) else: attachment_type = AttachmentType.TEXT formatted_contents = contents self.attach(name, formatted_contents, attachment_type) self.test.stop = now() self.test.status = status if status in FAILED_STATUSES: self.test.failure = Failure(message=get_exception_message( call.excinfo, pyteststatus, report), trace=report.longrepr) elif status in SKIPPED_STATUSES: skip_message = isinstance( report.longrepr, tuple) and report.longrepr[2] or report.wasxfail trim_msg_len = 89 short_message = skip_message.split('\n')[0][:trim_msg_len] # FIXME: see pytest.runner.pytest_runtest_makereport self.test.failure = Failure( message=(short_message + '...' * (len(skip_message) > trim_msg_len)), trace=status == Status.PENDING and report.longrepr or short_message != skip_message and skip_message or '')
def end_suite(self, name, attributes): self.testsuite.stop = now() for test in self.testsuite.tests: if self.suite_setup: test.steps.insert(0, self.suite_setup) if self.suite_teardown: test.steps.append(self.suite_teardown) self.suite_setup = None self.suite_teardown = None logfilename = '%s-testsuite.xml' % uuid.uuid4() # When running a folder, the folder itself is also considered a Suite # The full check depends on the availability of all the vars which are # only available when a Robot file has started. IsSuiteDirectory = os.path.isdir( BuiltIn().get_variable_value("${SUITE_SOURCE}")) logger.console('end_suite SUITE_SOURCE ' + BuiltIn().get_variable_value("${SUITE_SOURCE}")) logger.console('end_suite log dir ' + self.AllureImplc.logdir) if (not (IsSuiteDirectory)): with self.AllureImplc._reportfile(logfilename) as f: self.AllureImplc._write_xml(f, self.testsuite) return
def end_keyword(self, name, attributes): """ Stops the step at the top of ``self.stack`` Then adds it to the previous one, as this is a """ # Check to see if there are any items to add the log message to # this check is needed because otherwise Suite Setup may fail. if len(self.stack) > 0: if(attributes.get('type') == 'Keyword'): # pprint.pprint(attributes) step = self.stack.pop() if(attributes.get('status') == 'FAIL'): step.status = 'failed' elif(attributes.get('status') == 'PASS'): step.status = 'passed' step.stop = now() # Append the step to the previous item. This can be another step, or # another keyword. self.stack[-1].steps.append(step) return
def stop_step(self): """ Stops the step at the top of ``self.stack`` """ step = self.stack.pop() step.stop = now()