Skip to content

Commit 0e28e07

Browse files
authored
pytest-bdd beta (via allure-framework#447)
1 parent 6443d52 commit 0e28e07

File tree

17 files changed

+513
-0
lines changed

17 files changed

+513
-0
lines changed

allure-pytest-bdd/README.rst

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Feature: Scenario
2+
Scenario: Default labels
3+
Given feature file aaa with content:
4+
"""
5+
Feature: Scenario example
6+
Background:
7+
Given passed step1
8+
Scenario: Scenario example
9+
Given passed step
10+
When failed step
11+
Then passed step
12+
"""
13+
And dummy steps in conftest.py
14+
And test file with "Scenario example" scenario in example
15+
When run pytest-bdd with allure
16+
Then it has result for example scenario
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Feature: Scenario
2+
Scenario: Default labels
3+
Given feature file aaa with content:
4+
"""
5+
Feature: Scenario example
6+
Scenario Outline: Scenario example
7+
Given passed step
8+
When <status> step
9+
Then passed step
10+
11+
Examples:
12+
| status |
13+
| passed |
14+
| failed |
15+
"""
16+
And dummy steps in conftest.py
17+
And test file with "Scenario example" scenario in example
18+
When run pytest-bdd with allure
19+
Then it has result for example scenario
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Feature: Scenario
2+
Scenario: Default labels
3+
Given feature file aaa with content:
4+
"""
5+
Feature: Scenario example
6+
Scenario: Scenario example
7+
Given passed step
8+
When failed step
9+
Then passed step
10+
"""
11+
And dummy steps in conftest.py
12+
And test file with "Scenario example" scenario in example
13+
When run pytest-bdd with allure
14+
Then it has result for example scenario

allure-pytest-bdd/setup.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import os
2+
from setuptools import setup
3+
4+
PACKAGE = "allure-pytest-bdd"
5+
VERSION = "2.8.6b"
6+
7+
classifiers = [
8+
'Development Status :: 5 - Production/Stable',
9+
'Framework :: Pytest',
10+
'Intended Audience :: Developers',
11+
'License :: OSI Approved :: Apache Software License',
12+
'Topic :: Software Development :: Quality Assurance',
13+
'Topic :: Software Development :: Testing',
14+
]
15+
16+
install_requires = [
17+
"pytest>=4.5.0",
18+
"pytest-bdd>=3.0.0",
19+
"six>=1.9.0",
20+
"allure-python-commons==2.8.6"
21+
]
22+
23+
24+
def read(fname):
25+
return open(os.path.join(os.path.dirname(__file__), fname)).read()
26+
27+
28+
def main():
29+
setup(
30+
name=PACKAGE,
31+
version=VERSION,
32+
description="Allure pytest-bdd integration",
33+
url="https://github.com/allure-framework/allure-python",
34+
author="QAMetaSoftware, Stanislav Seliverstov",
35+
author_email="sseliverstov@qameta.io",
36+
license="Apache-2.0",
37+
classifiers=classifiers,
38+
keywords="allure reporting pytest",
39+
long_description=read('README.rst'),
40+
packages=["allure_pytest_bdd"],
41+
package_dir={"allure_pytest_bdd": "src"},
42+
entry_points={"pytest11": ["allure_pytest_bdd = allure_pytest_bdd.plugin"]},
43+
install_requires=install_requires
44+
)
45+
46+
if __name__ == '__main__':
47+
main()
48+

allure-pytest-bdd/src/__init__.py

Whitespace-only changes.

allure-pytest-bdd/src/plugin.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import allure_commons
2+
import os
3+
from allure_commons.logger import AllureFileLogger
4+
from .pytest_bdd_listener import PytestBDDListener
5+
6+
7+
def pytest_addoption(parser):
8+
parser.getgroup("reporting").addoption('--alluredir',
9+
action="store",
10+
dest="allure_report_dir",
11+
metavar="DIR",
12+
default=None,
13+
help="Generate Allure report in the specified directory (may not exist)")
14+
15+
parser.getgroup("reporting").addoption('--clean-alluredir',
16+
action="store_true",
17+
dest="clean_alluredir",
18+
help="Clean alluredir folder if it exists")
19+
20+
21+
def cleanup_factory(plugin):
22+
def clean_up():
23+
name = allure_commons.plugin_manager.get_name(plugin)
24+
allure_commons.plugin_manager.unregister(name=name)
25+
return clean_up
26+
27+
28+
def pytest_configure(config):
29+
report_dir = config.option.allure_report_dir
30+
clean = config.option.clean_alluredir
31+
32+
if report_dir:
33+
report_dir = os.path.abspath(report_dir)
34+
35+
pytest_bdd_listener = PytestBDDListener()
36+
config.pluginmanager.register(pytest_bdd_listener)
37+
38+
file_logger = AllureFileLogger(report_dir, clean)
39+
allure_commons.plugin_manager.register(file_logger)
40+
config.add_cleanup(cleanup_factory(file_logger))
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import pytest
2+
from allure_commons.utils import now
3+
from allure_commons.model2 import Label, Link
4+
from allure_commons.model2 import Status
5+
6+
from allure_commons.types import LabelType, AttachmentType
7+
from allure_commons.utils import platform_label
8+
from allure_commons.utils import host_tag, thread_tag
9+
from .utils import get_uuid
10+
from .utils import get_step_name
11+
from .utils import get_status_details
12+
from allure_commons.model2 import StatusDetails
13+
from functools import partial, reduce
14+
from allure_commons.lifecycle import AllureLifecycle
15+
from .utils import get_full_name
16+
17+
18+
class PytestBDDListener(object):
19+
def __init__(self):
20+
self.lifecycle = AllureLifecycle()
21+
self.host = host_tag()
22+
self.thread = thread_tag()
23+
24+
def _scenario_finalizer(self, scenario):
25+
for step in scenario.steps:
26+
step_uuid = get_uuid(str(id(step)))
27+
with self.lifecycle.update_step(uuid=step_uuid) as step_result:
28+
if step_result:
29+
step_result.status = Status.SKIPPED
30+
self.lifecycle.stop_step(uuid=step_uuid)
31+
32+
@pytest.hookimpl
33+
def pytest_bdd_before_scenario(self, request, feature, scenario):
34+
uuid = get_uuid(request.node.nodeid)
35+
full_name = get_full_name(feature, scenario)
36+
with self.lifecycle.schedule_test_case(uuid=uuid) as test_result:
37+
test_result.fullName = full_name
38+
test_result.start = now()
39+
test_result.labels.append(Label(name=LabelType.HOST, value=self.host))
40+
test_result.labels.append(Label(name=LabelType.THREAD, value=self.thread))
41+
test_result.labels.append(Label(name=LabelType.FRAMEWORK, value="pytest-bdd"))
42+
test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label()))
43+
test_result.labels.append(Label(name=LabelType.FEATURE, value=feature.name))
44+
45+
finalizer = partial(self._scenario_finalizer, scenario)
46+
request.node.addfinalizer(finalizer)
47+
48+
@pytest.hookimpl
49+
def pytest_bdd_after_scenario(self, request, feature, scenario):
50+
uuid = get_uuid(request.node.nodeid)
51+
with self.lifecycle.update_test_case(uuid=uuid) as test_result:
52+
test_result.stop = now()
53+
54+
@pytest.hookimpl
55+
def pytest_bdd_before_step_call(self, request, feature, scenario, step, step_func, step_func_args):
56+
parent_uuid = get_uuid(request.node.nodeid)
57+
uuid = get_uuid(str(id(step)))
58+
with self.lifecycle.start_step(parent_uuid=parent_uuid, uuid=uuid) as step_result:
59+
step_result.name = get_step_name(step)
60+
61+
@pytest.hookimpl
62+
def pytest_bdd_after_step(self, request, feature, scenario, step, step_func, step_func_args):
63+
uuid = get_uuid(str(id(step)))
64+
with self.lifecycle.update_step(uuid=uuid) as step_result:
65+
step_result.status = Status.PASSED
66+
self.lifecycle.stop_step(uuid=uuid)
67+
68+
@pytest.hookimpl
69+
def pytest_bdd_step_error(self, request, feature, scenario, step, step_func, step_func_args, exception):
70+
uuid = get_uuid(str(id(step)))
71+
with self.lifecycle.update_step(uuid=uuid) as step_result:
72+
step_result.status = Status.FAILED
73+
step_result.statusDetails = get_status_details(exception)
74+
self.lifecycle.stop_step(uuid=uuid)
75+
76+
@pytest.hookimpl
77+
def pytest_bdd_step_func_lookup_error(self, request, feature, scenario, step, exception):
78+
uuid = get_uuid(str(id(step)))
79+
with self.lifecycle.update_step(uuid=uuid) as step_result:
80+
step_result.status = Status.BROKEN
81+
self.lifecycle.stop_step(uuid=uuid)
82+
83+
@pytest.hookimpl(hookwrapper=True)
84+
def pytest_runtest_makereport(self, item, call):
85+
report = (yield).get_result()
86+
87+
status = reduce(lambda final_status, current_status: final_status or getattr(report, current_status, None),
88+
["failed", "passed", "skipped"])
89+
90+
status_details = StatusDetails(
91+
message=call.excinfo.exconly(),
92+
trace=report.longreprtext) if call.excinfo else None
93+
94+
uuid = get_uuid(report.nodeid)
95+
with self.lifecycle.update_test_case(uuid=uuid) as test_result:
96+
97+
if test_result and report.when == "setup":
98+
test_result.status = status
99+
test_result.statusDetails = status_details
100+
101+
if report.when == "call" and test_result:
102+
if test_result.status not in [Status.PASSED, Status.FAILED]:
103+
test_result.status = status
104+
test_result.statusDetails = status_details
105+
106+
if report.when == "teardown" and test_result:
107+
if test_result.status == Status.PASSED and status != Status.PASSED:
108+
test_result.status = status
109+
test_result.statusDetails = status_details
110+
111+
if report.when == 'teardown':
112+
self.lifecycle.write_test_case(uuid=uuid)

allure-pytest-bdd/src/utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import os
2+
from uuid import UUID
3+
from allure_commons.utils import md5
4+
from allure_commons.model2 import StatusDetails
5+
from allure_commons.utils import format_exception
6+
7+
8+
def get_step_name(step):
9+
return "{step_keyword} {step_name}".format(step_keyword=step.keyword, step_name=step.name)
10+
11+
12+
def get_full_name(feature, scenario):
13+
feature_path = os.path.normpath(feature.rel_filename)
14+
return "{feature}:{scenario}".format(feature=feature_path, scenario=scenario.name)
15+
16+
17+
def get_uuid(*args):
18+
return str(UUID(md5(*args)))
19+
20+
21+
def get_status_details(exception):
22+
message = str(exception)
23+
trace = format_exception(type(exception), exception)
24+
return StatusDetails(message=message, trace=trace) if message or trace else None

allure-pytest-bdd/test/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)