diff --git a/src/pytest_bdd/plugin.py b/src/pytest_bdd/plugin.py index 486cdf87..7ad8ff63 100644 --- a/src/pytest_bdd/plugin.py +++ b/src/pytest_bdd/plugin.py @@ -21,6 +21,10 @@ from .parser import Feature, Scenario, Step +# Counter to track how many times pytest initialization was started +_INIT_COUNT = 0 + + def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: """Register plugin hooks.""" from pytest_bdd import hooks @@ -52,6 +56,8 @@ def _pytest_bdd_example() -> dict: def pytest_addoption(parser: Parser) -> None: """Add pytest-bdd options.""" + global _INIT_COUNT + _INIT_COUNT += 1 add_bdd_ini(parser) cucumber_json.add_options(parser) generation.add_options(parser) @@ -66,14 +72,19 @@ def add_bdd_ini(parser: Parser) -> None: def pytest_configure(config: Config) -> None: """Configure all subplugins.""" CONFIG_STACK.append(config) + assert _INIT_COUNT == len(CONFIG_STACK) cucumber_json.configure(config) gherkin_terminal_reporter.configure(config) def pytest_unconfigure(config: Config) -> None: """Unconfigure all subplugins.""" - CONFIG_STACK.pop() - cucumber_json.unconfigure(config) + global _INIT_COUNT + assert len(CONFIG_STACK) <= _INIT_COUNT + if len(CONFIG_STACK) == _INIT_COUNT: + CONFIG_STACK.pop() + cucumber_json.unconfigure(config) + _INIT_COUNT -= 1 @pytest.hookimpl(hookwrapper=True) diff --git a/tests/test_hooks.py b/tests/test_hooks.py index e3fda90c..53b44f72 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -135,3 +135,21 @@ def pytest_bdd_after_scenario(request, feature, scenario): assert scenario_1.name == "Scenario 1" assert scenario_2.name == "Scenario 2" + + +def test_pytest_unconfigure_without_configure(pytester): + """ + Simulate a plugin forcing an exit during configuration before bdd is configured + https://github.com/pytest-dev/pytest-bdd/issues/362 + """ + pytester.makeconftest( + """ + import pytest + + def pytest_configure(config): + pytest.exit("Exit during configure", 0) + """ + ) + + result = pytester.runpytest() + assert result.ret == 0