class PytestBDDListener(object): def __init__(self): self.lifecycle = AllureLifecycle() self.host = host_tag() self.thread = thread_tag() def _scenario_finalizer(self, scenario): for step in scenario.steps: step_uuid = get_uuid(str(id(step))) with self.lifecycle.update_step(uuid=step_uuid) as step_result: if step_result: step_result.status = Status.SKIPPED self.lifecycle.stop_step(uuid=step_uuid) @pytest.hookimpl def pytest_bdd_before_scenario(self, request, feature, scenario): uuid = get_uuid(request.node.nodeid) full_name = get_full_name(feature, scenario) name = get_name(request.node, scenario) with self.lifecycle.schedule_test_case(uuid=uuid) as test_result: test_result.fullName = full_name test_result.name = name test_result.start = now() test_result.labels.append(Label(name=LabelType.HOST, value=self.host)) test_result.labels.append(Label(name=LabelType.THREAD, value=self.thread)) test_result.labels.append(Label(name=LabelType.FRAMEWORK, value="pytest-bdd")) test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) test_result.labels.append(Label(name=LabelType.FEATURE, value=feature.name)) test_result.parameters = get_params(request.node) finalizer = partial(self._scenario_finalizer, scenario) request.node.addfinalizer(finalizer) @pytest.hookimpl def pytest_bdd_after_scenario(self, request, feature, scenario): uuid = get_uuid(request.node.nodeid) with self.lifecycle.update_test_case(uuid=uuid) as test_result: test_result.stop = now() @pytest.hookimpl def pytest_bdd_before_step_call(self, request, feature, scenario, step, step_func, step_func_args): parent_uuid = get_uuid(request.node.nodeid) uuid = get_uuid(str(id(step))) with self.lifecycle.start_step(parent_uuid=parent_uuid, uuid=uuid) as step_result: step_result.name = get_step_name(request.node, step) @pytest.hookimpl def pytest_bdd_after_step(self, request, feature, scenario, step, step_func, step_func_args): uuid = get_uuid(str(id(step))) with self.lifecycle.update_step(uuid=uuid) as step_result: step_result.status = Status.PASSED self.lifecycle.stop_step(uuid=uuid) @pytest.hookimpl def pytest_bdd_step_error(self, request, feature, scenario, step, step_func, step_func_args, exception): uuid = get_uuid(str(id(step))) with self.lifecycle.update_step(uuid=uuid) as step_result: step_result.status = Status.FAILED step_result.statusDetails = get_status_details(exception) self.lifecycle.stop_step(uuid=uuid) @pytest.hookimpl def pytest_bdd_step_func_lookup_error(self, request, feature, scenario, step, exception): uuid = get_uuid(str(id(step))) with self.lifecycle.update_step(uuid=uuid) as step_result: step_result.status = Status.BROKEN self.lifecycle.stop_step(uuid=uuid) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(self, item, call): report = (yield).get_result() status = get_pytest_report_status(report) status_details = StatusDetails( message=call.excinfo.exconly(), trace=report.longreprtext) if call.excinfo else None uuid = get_uuid(report.nodeid) with self.lifecycle.update_test_case(uuid=uuid) as test_result: if test_result and report.when == "setup": test_result.status = status test_result.statusDetails = status_details if report.when == "call" and test_result: if test_result.status not in [Status.PASSED, Status.FAILED]: test_result.status = status test_result.statusDetails = status_details if report.when == "teardown" and test_result: if test_result.status == Status.PASSED and status != Status.PASSED: test_result.status = status test_result.statusDetails = status_details if report.when == 'teardown': self.lifecycle.write_test_case(uuid=uuid) @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.lifecycle.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): self.lifecycle.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension)
class Allure(Plugin): configSection = 'allure' commandLineSwitch = (None, "allure", "Generate an Allure report") def __init__(self, *args, **kwargs): super(Allure, self).__init__(*args, **kwargs) self._host = host_tag() self._thread = thread_tag() self.lifecycle = AllureLifecycle() self.logger = AllureFileLogger("allure-result") self.listener = AllureListener(self.lifecycle) def registerInSubprocess(self, event): self.unregister_allure_plugins() event.pluginClasses.append(self.__class__) def startSubprocess(self, event): self.register_allure_plugins() def stopSubprocess(self, event): self.unregister_allure_plugins() def register_allure_plugins(self): plugin_manager.register(self.listener) plugin_manager.register(self.logger) def unregister_allure_plugins(self): plugin_manager.unregister(plugin=self.listener) plugin_manager.unregister(plugin=self.logger) def is_registered(self): return all([ plugin_manager.is_registered(self.listener), plugin_manager.is_registered(self.logger) ]) def startTestRun(self, event): self.register_allure_plugins() def afterTestRun(self, event): self.unregister_allure_plugins() def startTest(self, event): if self.is_registered(): with self.lifecycle.schedule_test_case() as test_result: test_result.name = name(event) test_result.start = timestamp_millis(event.startTime) test_result.fullName = fullname(event) test_result.testCaseId = md5(test_result.fullName) test_result.historyId = md5(event.test.id()) test_result.labels.extend(labels(event.test)) test_result.labels.append( Label(name=LabelType.HOST, value=self._host)) test_result.labels.append( Label(name=LabelType.THREAD, value=self._thread)) test_result.labels.append( Label(name=LabelType.FRAMEWORK, value='nose2')) test_result.labels.append( Label(name=LabelType.LANGUAGE, value=platform_label())) test_result.parameters = params(event) def stopTest(self, event): if self.is_registered(): with self.lifecycle.update_test_case() as test_result: test_result.stop = timestamp_millis(event.stopTime) self.lifecycle.write_test_case() def testOutcome(self, event): if self.is_registered(): with self.lifecycle.update_test_case() as test_result: if event.outcome == result.PASS and event.expected: test_result.status = Status.PASSED elif event.outcome == result.PASS and not event.expected: test_result.status = Status.PASSED test_result.statusDetails = StatusDetails( message="test passes unexpectedly") elif event.outcome == result.FAIL and not event.expected: test_result.status = Status.FAILED test_result.statusDetails = status_details(event) elif event.outcome == result.ERROR: test_result.status = Status.BROKEN test_result.statusDetails = status_details(event) elif event.outcome == result.SKIP: test_result.status = Status.SKIPPED test_result.statusDetails = status_details(event)