Skip to content

Commit 690c6ea

Browse files
authored
Fix regression: allure_pytest.utils.allure_title crashes if obj attr doesn't exist (Fixes #733) (#734)
1 parent 86cff25 commit 690c6ea

File tree

9 files changed

+59
-40
lines changed

9 files changed

+59
-40
lines changed

allure-pytest/src/utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ def get_marker_value(item, keyword):
2727

2828

2929
def allure_title(item):
30-
return getattr(item.obj, "__allure_display_name__", None)
30+
return getattr(
31+
getattr(item, "obj", None),
32+
"__allure_display_name__",
33+
None
34+
)
3135

3236

3337
def allure_description(item):

tests/allure_behave/behave_runner.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def load_step_definitions(self, extra_step_paths=None):
7777
"step_matcher": matchers.step_matcher,
7878
}
7979

80-
# To support the decorators (i.e., @given) with no imports
80+
# To support the decorators (e.g., @given) with no imports
8181
setup_step_decorators(step_globals, self.step_registry)
8282

8383
default_matcher = matchers.current_matcher
@@ -113,7 +113,7 @@ class AllureBehaveRunner(AllureFrameworkRunner):
113113
LOGGER_PATH = "allure_behave.formatter.AllureFileLogger"
114114

115115
def __init__(self, request: FixtureRequest, pytester: Pytester):
116-
super().__init__(request, pytester)
116+
super().__init__(request, pytester, AllureBehaveRunner.LOGGER_PATH)
117117

118118
def run_behave(
119119
self,
@@ -181,7 +181,6 @@ def run_behave(
181181
testplan_content=testplan_content,
182182
testplan_path=testplan_path,
183183
testplan_rst_id=testplan_rst_id,
184-
logger_path=AllureBehaveRunner.LOGGER_PATH,
185184
options=options
186185
)
187186

tests/allure_nose2/nose2_runner.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,14 @@ class AllureNose2Runner(AllureFrameworkRunner):
1010
LOGGER_PATH = "allure_nose2.plugin.AllureFileLogger"
1111

1212
def __init__(self, request: FixtureRequest, pytester: Pytester):
13-
super().__init__(request, pytester)
13+
super().__init__(request, pytester, AllureNose2Runner.LOGGER_PATH)
1414

1515
def run_docstring(self):
1616
docstring = self._find_docstring()
1717
example_code = script_from_examples(docstring)
1818
spec = importlib.machinery.ModuleSpec(self.request.node.name, None)
1919
module = importlib.util.module_from_spec(spec)
20-
return self._run(
21-
module,
22-
example_code,
23-
logger_path=AllureNose2Runner.LOGGER_PATH
24-
)
20+
return self._run(module, example_code)
2521

2622
def _run_framework(self, module, example):
2723
# We execute the example here because the _run_framework runs in a

tests/allure_pytest/defects/__init__.py

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from allure_pytest.utils import allure_title
2+
3+
4+
def test_no_allure_title_error_if_item_obj_missing():
5+
item_with_no_obj_attr_stub = object()
6+
7+
assert allure_title(item_with_no_obj_attr_stub) is None

tests/allure_pytest/pytest_runner.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ class AllurePytestRunner(AllureFrameworkRunner):
2222
DOCTEST_RESULT_KEY = StashKey()
2323

2424
def __init__(self, request: FixtureRequest, pytester: Pytester):
25-
self.logger_path = AllurePytestRunner.LOGGER_PATH
26-
super().__init__(request, pytester)
25+
super().__init__(request, pytester, AllurePytestRunner.LOGGER_PATH)
2726
self.select_plugins("allure_pytest")
2827

2928
def enable_plugins(self, *plugins: str) -> None:
@@ -159,11 +158,7 @@ def run_pytest(
159158
*cli_args
160159
]
161160
self.__generate_testfiles(testfile_literals)
162-
return self._run(
163-
pytest_args,
164-
testplan_content=testplan,
165-
logger_path=self.logger_path
166-
)
161+
return self._run(pytest_args, testplan_content=testplan)
167162

168163
def _run_framework(self, options):
169164
with altered_env(PYTEST_DISABLE_PLUGIN_AUTOLOAD="true"):

tests/allure_pytest_bdd/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
@pytest.fixture
66
def allure_pytest_bdd_runner(request, pytester):
77
runner = AllurePytestRunner(request, pytester)
8-
runner.logger_path = "allure_pytest_bdd.plugin.AllureFileLogger"
8+
runner.imported_logger_paths = ["allure_pytest_bdd.plugin.AllureFileLogger"]
99
runner.select_plugins("pytest-bdd", "allure_pytest_bdd")
1010
yield runner

tests/allure_robotframework/robot_runner.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class AllureRobotRunner(AllureFrameworkRunner):
1111
LOGGER_PATH = "allure_robotframework.robot_listener.AllureFileLogger"
1212

1313
def __init__(self, request: FixtureRequest, pytester: Pytester):
14-
super().__init__(request, pytester)
14+
super().__init__(request, pytester, AllureRobotRunner.LOGGER_PATH)
1515

1616
def run_robotframework(
1717
self,
@@ -75,7 +75,6 @@ def run_robotframework(
7575
testplan_content=testplan_content,
7676
testplan_path=testplan_path,
7777
testplan_rst_id=testplan_rst_id,
78-
logger_path=AllureRobotRunner.LOGGER_PATH,
7978
options=self.__resolve_options(options)
8079
)
8180

tests/e2e.py

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import shutil
1313
import warnings
1414
from abc import abstractmethod
15-
from contextlib import contextmanager
15+
from contextlib import contextmanager, ExitStack
1616
from pathlib import Path
1717
from pytest import FixtureRequest, Pytester, MonkeyPatch
1818
from typing import Tuple, Mapping, TypeVar, Generator, Callable, Union
@@ -54,7 +54,7 @@ def allure_plugin_context():
5454

5555
@contextmanager
5656
def allure_in_memory_context(
57-
path: str = None
57+
*paths: str
5858
) -> Generator[AllureMemoryLogger, None, None]:
5959
"""Creates a context to test an allure integration.
6060
@@ -70,28 +70,30 @@ def allure_in_memory_context(
7070
restored.
7171
7272
Arguments:
73-
path (str): a path to a class to replace.
74-
Defaults to :code:`"allure_commons.logger.AllureFileLogger"`.
75-
Provide this argument if the integration under test imports the
76-
logger using the
77-
:code:`from allure_commons.logger import AllureFileLogger` syntax.
73+
*paths (str): paths to classes to replace with the in-memory logger in
74+
addition to :code:`"allure_commons.logger.AllureFileLogger"`.
75+
Provide these if the integration under test imports the logger using
76+
the :code:`from allure_commons.logger import AllureFileLogger`
77+
syntax.
7878
7979
Yields:
80-
AllureMemoryLogger: an instance of the in-memory logger, where an output
81-
will be collected.
80+
AllureMemoryLogger: an instance of the in-memory logger, where the
81+
output is collected.
8282
8383
"""
8484

85-
if path is None:
86-
path = "allure_commons.logger.AllureFileLogger"
87-
8885
# Plugin context must be set first, because mock patching may cause
8986
# module loading, thus, side effects, including allure decorators evaluation
9087
# (and that requires all plugins of nested allure to already be in place).
88+
paths = ("allure_commons.logger.AllureFileLogger",) + paths
9189
with allure_plugin_context():
92-
with mock.patch(path) as ReporterMock:
93-
ReporterMock.return_value = AllureMemoryLogger()
94-
yield ReporterMock.return_value
90+
logger = AllureMemoryLogger()
91+
with ExitStack() as stack:
92+
for path in paths:
93+
stack.enter_context(
94+
mock.patch(path)
95+
).return_value = logger
96+
yield logger
9597

9698

9799
class AllureFileContextValue:
@@ -336,20 +338,38 @@ class AllureFrameworkRunner:
336338
"""An abstract base class for framework test runners to test allure
337339
integrations.
338340
341+
Attributes:
342+
request (FixtureRequest): an instance of the request fixture.
343+
pytester (Pytester): an instance of the pytester fixture.
344+
allure_results (AllureMemoryLogger | AllureReport): the latest collected
345+
allure results.
346+
in_memory (bool): if `True`, the next run collects the results in memory
347+
(:attr:`AllureFrameworkRunner.allure_results` is AllureMemoryLogger).
348+
Otherwise, the next run creates allure result files and collects the
349+
report from them
350+
(:attr:`AllureFrameworkRunner.allure_results` is AllureReport).
351+
*imported_logger_paths: a sequence of paths to provide to
352+
:func:`allure_in_memory_context`.
353+
339354
"""
340-
def __init__(self, request: FixtureRequest, pytester: Pytester):
355+
def __init__(
356+
self,
357+
request: FixtureRequest,
358+
pytester: Pytester,
359+
*imported_logger_paths
360+
):
341361
self.request = request
342362
self.pytester = pytester
343363
self.allure_results = None
344364
self.in_memory = True
365+
self.imported_logger_paths = list(imported_logger_paths)
345366

346367
def _run(
347368
self,
348369
*args,
349370
testplan_content: dict = None,
350371
testplan_path: PathlikeT = None,
351372
testplan_rst_id: str = None,
352-
logger_path: str = None,
353373
**kwargs
354374
) -> AllureMemoryLogger:
355375
"""Runs the framework and collect the allure results.
@@ -380,7 +400,6 @@ def _run(
380400
)
381401
with altered_env(ALLURE_TESTPLAN_PATH=testplan_path):
382402
output = self.__run_and_collect_results_in_memory(
383-
logger_path,
384403
args,
385404
kwargs
386405
) if self.in_memory else self.__run_and_collect_results_from_fs(
@@ -572,8 +591,8 @@ def _cache_docstring_test_result(
572591
node.stash[cache_key] = result
573592
return result
574593

575-
def __run_and_collect_results_in_memory(self, logger_path, args, kwargs):
576-
with allure_in_memory_context(logger_path) as output:
594+
def __run_and_collect_results_in_memory(self, args, kwargs):
595+
with allure_in_memory_context(*self.imported_logger_paths) as output:
577596
self._run_framework(*args, **kwargs)
578597
return output
579598

0 commit comments

Comments
 (0)