Skip to content

fix(behave): add cleanup; fix invalid call from AllureHooks #860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 6, 2025
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
13 changes: 11 additions & 2 deletions allure-behave/src/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ def __init__(self, stream_opener, config):
super(AllureFormatter, self).__init__(stream_opener, config)

self.listener = AllureListener(config)
file_logger = AllureFileLogger(self.stream_opener.name)
self.file_logger = AllureFileLogger(self.stream_opener.name)

allure_commons.plugin_manager.register(self.listener)
allure_commons.plugin_manager.register(file_logger)
allure_commons.plugin_manager.register(self.file_logger)

self.testplan = get_testplan()

Expand Down Expand Up @@ -45,5 +45,14 @@ def result(self, result):
def eof(self):
self.listener.stop_feature()

def close(self):
try:
super().close()
finally:
for plugin in [self.file_logger, self.listener]:
name = allure_commons.plugin_manager.get_name(plugin)
if allure_commons.plugin_manager.has_plugin(name):
allure_commons.plugin_manager.unregister(name=name)

def close_stream(self):
self.listener.stop_session()
17 changes: 14 additions & 3 deletions allure-behave/src/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from behave.configuration import Configuration

HOOKS = [
"after_all",
"before_feature",
"after_feature",
"before_scenario",
Expand Down Expand Up @@ -42,15 +43,25 @@ def allure_report(result_dir="allure_results"):
class AllureHooks:
def __init__(self, result_dir):
self.listener = AllureListener(Configuration())
self.plugins = []

if not hasattr(_storage, 'file_logger'):
_storage.file_logger = AllureFileLogger(result_dir)
allure_commons.plugin_manager.register(_storage.file_logger)
logger = AllureFileLogger(result_dir)
_storage.file_logger = logger
allure_commons.plugin_manager.register(logger)
self.plugins.append(logger)

allure_commons.plugin_manager.register(self.listener)
self.plugins.append(self.listener)

def after_all(self, context):
for plugin in self.plugins:
name = allure_commons.plugin_manager.get_name(plugin)
if allure_commons.plugin_manager.has_plugin(name):
allure_commons.plugin_manager.unregister(name=name)

def before_feature(self, context, feature):
self.listener.start_feature()
self.listener.start_file()

def after_feature(self, context, feature):
self.listener.stop_feature()
Expand Down
54 changes: 30 additions & 24 deletions tests/allure_behave/behave_runner.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from contextlib import contextmanager
import behave.step_registry
import sys

Expand All @@ -16,7 +17,8 @@
from allure_behave.formatter import AllureFormatter


def __fix_behave_in_memory_run():
@contextmanager
def _fixed_in_memory_run():
# Behave has poor support for consecutive prigrammatic runs. This is due to
# how step decorators are cached.
# There are three ways to introduce behave step decorators (i.e., @given)
Expand Down Expand Up @@ -50,6 +52,10 @@ def __fixed_add_step_definition(self, *args, **kwargs):

StepRegistry.add_step_definition = __fixed_add_step_definition

yield

StepRegistry.add_step_definition = original_add_step_definition


class _InMemoryBehaveRunner(Runner):
def __init__(self, features, steps, environment, args=None):
Expand Down Expand Up @@ -159,32 +165,32 @@ def run_behave(
:attr:`allure_results` attribute.

"""
return self._run(
self._get_all_content(
paths=feature_paths,
literals=feature_literals,
rst_ids=feature_rst_ids
),
self._get_all_content(
paths=step_paths,
literals=step_literals,
rst_ids=step_rst_ids
),
self._resolve_content(
path=environment_path,
literal=environment_literal,
rst_id=environment_rst_id
),
testplan_content=testplan_content,
testplan_path=testplan_path,
testplan_rst_id=testplan_rst_id,
options=options
)

with _fixed_in_memory_run():
return self._run(
self._get_all_content(
paths=feature_paths,
literals=feature_literals,
rst_ids=feature_rst_ids
),
self._get_all_content(
paths=step_paths,
literals=step_literals,
rst_ids=step_rst_ids
),
self._resolve_content(
path=environment_path,
literal=environment_literal,
rst_id=environment_rst_id
),
testplan_content=testplan_content,
testplan_path=testplan_path,
testplan_rst_id=testplan_rst_id,
options=options
)

def _run_framework(self, features, steps, environment, options):
_InMemoryBehaveRunner(features, steps, environment, options).run()


__fix_behave_in_memory_run()

__all__ = ["AllureBehaveRunner"]
38 changes: 38 additions & 0 deletions tests/allure_behave/defects/issue858_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import allure
import shlex

from tests.allure_behave.behave_runner import AllureBehaveRunner
from ...e2e import allure_file_context

from behave import __main__ as runner


@allure.issue("858")
def test_test_results_leak(behave_runner: AllureBehaveRunner):
feature_path = behave_runner.pytester.makefile(
".feature",
(
"""
Feature: Foo
Scenario: Bar
Given baz
"""
),
)
behave_runner.pytester.makefile(
".py",
**{"steps/steps": "given('baz')(lambda *_: None)"},
)

args = shlex.join([
feature_path.name,
"-f", "allure_behave.formatter:AllureFormatter",
"-o", "allure-results",
"--no-summary",
])

with allure_file_context("allure-results") as context:
runner.main(args)
runner.main(args)

assert len(context.allure_results.test_cases) == 2