Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions allure-pytest-bdd/src/pytest_bdd_listener.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import pytest

from collections import defaultdict

from allure_commons.utils import now
from allure_commons.model2 import Label
from allure_commons.model2 import Status
from allure_commons.types import LabelType, AttachmentType
from allure_commons.utils import platform_label
from allure_commons.utils import host_tag, thread_tag
from allure_commons.utils import md5
from allure_commons.utils import uuid4

from .steps import get_step_uuid
from .steps import process_gherkin_step_args
Expand All @@ -29,7 +32,6 @@
from .utils import get_pytest_report_status
from .utils import get_scenario_status_details
from .utils import get_test_name
from .utils import get_uuid
from .utils import post_process_test_result

from functools import partial
Expand All @@ -40,11 +42,12 @@ def __init__(self, lifecycle):
self.lifecycle = lifecycle
self.host = host_tag()
self.thread = thread_tag()
self.nodeid_to_uuid = defaultdict(uuid4)

@pytest.hookimpl
def pytest_bdd_before_scenario(self, request, feature, scenario):
item = request.node
uuid = get_uuid(item.nodeid)
uuid = self.nodeid_to_uuid[item.nodeid]

outline_params = get_outline_params(item)
pytest_params = get_pytest_params(item)
Expand Down Expand Up @@ -74,18 +77,19 @@ def pytest_bdd_before_scenario(self, request, feature, scenario):
test_result.links.extend(get_allure_links(item))
test_result.parameters.extend(convert_params(outline_params, pytest_params))

finalizer = partial(report_remaining_steps, self.lifecycle, item)
finalizer = partial(report_remaining_steps, self.lifecycle, item, uuid)
item.addfinalizer(finalizer)

@pytest.hookimpl
def pytest_bdd_after_scenario(self, request, feature, scenario):
uuid = get_uuid(request.node.nodeid)
uuid = self.nodeid_to_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(self, request, feature, scenario, step, step_func):
start_gherkin_step(self.lifecycle, request.node, step, step_func)
uuid = self.nodeid_to_uuid[request.node.nodeid]
start_gherkin_step(self.lifecycle, request.node, uuid, step, step_func)

@pytest.hookimpl
def pytest_bdd_before_step_call(self, request, feature, scenario, step, step_func, step_func_args):
Expand All @@ -101,7 +105,8 @@ def pytest_bdd_step_error(self, request, feature, scenario, step, step_func, ste

@pytest.hookimpl
def pytest_bdd_step_func_lookup_error(self, request, feature, scenario, step, exception):
report_undefined_step(self.lifecycle, request.node, step, exception)
uuid = self.nodeid_to_uuid[request.node.nodeid]
report_undefined_step(self.lifecycle, request.node, uuid, step, exception)

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(self, item, call):
Expand All @@ -112,7 +117,8 @@ def pytest_runtest_makereport(self, item, call):
status = get_pytest_report_status(report, excinfo)
status_details = get_scenario_status_details(report, excinfo)

uuid = get_uuid(report.nodeid)
uuid = self.nodeid_to_uuid[report.nodeid]

with self.lifecycle.update_test_case(uuid=uuid) as test_result:

if test_result and report.when == "setup":
Expand Down Expand Up @@ -143,3 +149,4 @@ def pytest_runtest_makereport(self, item, call):

if report.when == "teardown":
self.lifecycle.write_test_case(uuid=uuid)
del self.nodeid_to_uuid[report.nodeid]
25 changes: 14 additions & 11 deletions allure-pytest-bdd/src/steps.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from uuid import UUID

from allure import attachment_type
from allure_commons.model2 import StatusDetails
from allure_commons.model2 import Status
from allure_commons.model2 import Parameter
from allure_commons.utils import format_exception
from allure_commons.utils import represent
from allure_commons.utils import md5

from .storage import get_saved_params
from .storage import get_test_data
from .storage import save_reported_step
from .utils import attach_data
from .utils import format_csv
from .utils import get_allure_title
from .utils import get_uuid
from .utils import get_status
from .utils import get_status_details

Expand All @@ -32,7 +34,7 @@ def get_allure_title_of_step(item, step_func, step_func_args):


def get_step_uuid(step):
return get_uuid(str(id(step)))
return str(UUID(md5(id(step))))


def start_step(lifecycle, step_uuid, title, params=None, parent_uuid=None):
Expand All @@ -57,15 +59,15 @@ def stop_step(lifecycle, uuid, status=None, status_details=None, exception=None,
return True


def start_gherkin_step(lifecycle, item, step, step_func=None, step_uuid=None):
def start_gherkin_step(lifecycle, item, test_uuid, step, step_func=None, step_uuid=None):
if step_uuid is None:
step_uuid = get_step_uuid(step)

start_step(
lifecycle,
step_uuid=step_uuid,
title=get_step_name(item, step, step_func),
parent_uuid=get_uuid(item.nodeid),
parent_uuid=test_uuid,
)


Expand Down Expand Up @@ -152,22 +154,23 @@ def stop_gherkin_step(lifecycle, item, step_uuid, **kwargs):
return res


def ensure_gherkin_step_reported(lifecycle, item, step, step_uuid=None, **kwargs):
def ensure_gherkin_step_reported(lifecycle, item, test_uuid, step, step_uuid=None, **kwargs):

if not step_uuid:
step_uuid = get_step_uuid(step)

if stop_gherkin_step(lifecycle, item, step_uuid, **kwargs):
return

start_gherkin_step(lifecycle, item, step, step_uuid=step_uuid)
start_gherkin_step(lifecycle, item, test_uuid, step, step_uuid=step_uuid)
stop_gherkin_step(lifecycle, item, step_uuid, **kwargs)


def report_undefined_step(lifecycle, item, step, exception):
def report_undefined_step(lifecycle, item, test_uuid, step, exception):
ensure_gherkin_step_reported(
lifecycle,
item,
test_uuid,
step,
status=Status.BROKEN,
status_details=StatusDetails(
Expand All @@ -176,7 +179,7 @@ def report_undefined_step(lifecycle, item, step, exception):
)


def report_remaining_steps(lifecycle, item):
def report_remaining_steps(lifecycle, item, test_uuid):
test_data = get_test_data(item)
scenario = test_data.scenario
excinfo = test_data.excinfo
Expand All @@ -185,12 +188,12 @@ def report_remaining_steps(lifecycle, item):
for step in scenario.steps:
step_uuid = get_step_uuid(step)
if step_uuid not in reported_steps:
__report_remaining_step(lifecycle, item, step, step_uuid, excinfo)
__report_remaining_step(lifecycle, item, test_uuid, step, step_uuid, excinfo)
excinfo = None # Only show the full message and traceback once


def __report_remaining_step(lifecycle, item, step, step_uuid, excinfo):
args = [lifecycle, item, step, step_uuid]
def __report_remaining_step(lifecycle, item, test_uuid, step, step_uuid, excinfo):
args = [lifecycle, item, test_uuid, step, step_uuid]
kwargs = {
"exception": excinfo.value,
"exception_type": excinfo.type,
Expand Down
5 changes: 0 additions & 5 deletions allure-pytest-bdd/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import io
import os
from urllib.parse import urlparse
from uuid import UUID
from pathlib import Path

import pytest
Expand Down Expand Up @@ -182,10 +181,6 @@ def get_title_path(request, feature):
return [*parts[:-1], feature.name or parts[-1]]


def get_uuid(*args):
return str(UUID(md5(*args)))


def get_status(exception):
if exception:
if isinstance(exception, (pytest.skip.Exception, pytest.xfail.Exception)):
Expand Down
42 changes: 40 additions & 2 deletions tests/allure_pytest_bdd/acceptance/scenario_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" ./allure-pytest-bdd/examples/simple-scenario """

from hamcrest import assert_that
from allure import issue
from hamcrest import assert_that, not_, equal_to
from tests.allure_pytest.pytest_runner import AllurePytestRunner
from allure_commons_test.report import has_test_case
from allure_commons_test.result import with_status
Expand Down Expand Up @@ -42,7 +43,7 @@ def then_the_postconditions_are_held():

output = allure_pytest_bdd_runner.run_pytest(
("scenario.feature", feature_content),
steps_content
steps_content,
)

assert_that(
Expand All @@ -56,3 +57,40 @@ def then_the_postconditions_are_held():
has_history_id()
)
)

@issue("925")
def test_uuid_not_reused_across_runs(allure_pytest_bdd_runner: AllurePytestRunner):
feature_content = (
"""
Feature: Foo
Scenario: Bar
Given noop
"""
)
steps_content = (
"""
from pytest_bdd import scenario, given, when, then

@scenario("foo.feature", "Bar")
def test_bar():
pass

@given("noop")
def given_noop():
pass
"""
)

output1 = allure_pytest_bdd_runner.run_pytest(
("foo.feature", feature_content),
steps_content
)
output2 = allure_pytest_bdd_runner.run_pytest(
("foo.feature", feature_content),
steps_content
)

uuid1 = output1.test_cases[0]["uuid"]
uuid2 = output2.test_cases[0]["uuid"]

assert_that(uuid1, not_(equal_to(uuid2)))
Loading