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)
Beispiel #2
0
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)