diff --git a/CHANGES.rst b/CHANGES.rst index 2706c8aeb..d02044f10 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,7 @@ Added Changed +++++++ +* The code generation template (``pytest-bdd generate`` and ``--generate-missing``) now uses the ``scenarios()`` shortcut instead of one ``@scenario()`` decorator per scenario (`#535 `_, `#796 `_). * Relaxed `gherkin-official` dependency requirement to `>=29.0.0` to allow for newer versions of the `gherkin-official` package. * Excluded `gherkin-official` `31.0.0` and `32.0.0`, which crash with ``StopIteration`` when parsing empty descriptions (fixed upstream in `32.0.1`). diff --git a/src/pytest_bdd/templates/test.py.mak b/src/pytest_bdd/templates/test.py.mak index 9f7901539..3b276db6d 100644 --- a/src/pytest_bdd/templates/test.py.mak +++ b/src/pytest_bdd/templates/test.py.mak @@ -3,20 +3,20 @@ from pytest_bdd import ( given, - scenario, + scenarios, then, when, ) - -% endif -% for scenario in sorted(scenarios, key=lambda scenario: scenario.name): -@scenario('${scenario.feature.rel_filename}', ${ make_string_literal(scenario.name)}) -def test_${ make_python_name(scenario.name)}(): - ${make_python_docstring(scenario.name)} +<% + feature_paths = sorted(set(f.rel_filename for f in features)) +%>\ +% for feature_path in feature_paths: +scenarios('${feature_path}') +% endfor -% endfor +% endif % for step in steps: @${step.type}(${ make_string_literal(step.name)}) def _(): diff --git a/tests/scripts/test_generate.py b/tests/scripts/test_generate.py index e194076d1..5bfd83d53 100644 --- a/tests/scripts/test_generate.py +++ b/tests/scripts/test_generate.py @@ -42,15 +42,12 @@ def test_generate(pytester, monkeypatch, capsys): from pytest_bdd import ( given, - scenario, + scenarios, then, when, ) - - @scenario('scripts/generate.feature', 'Given and when using the same fixture should not evaluate it twice') - def test_given_and_when_using_the_same_fixture_should_not_evaluate_it_twice(): - """Given and when using the same fixture should not evaluate it twice.""" + scenarios('scripts/generate.feature') @given('1 have a fixture (appends 1 to a list) in reuse syntax') @@ -108,15 +105,12 @@ def test_generate_with_quotes(pytester): from pytest_bdd import ( given, - scenario, + scenarios, then, when, ) - - @scenario('generate_with_quotes.feature', 'A step definition with quotes should be escaped as needed') - def test_a_step_definition_with_quotes_should_be_escaped_as_needed(): - """A step definition with quotes should be escaped as needed.""" + scenarios('generate_with_quotes.feature') @given('I have a fixture with "double" quotes') @@ -157,6 +151,37 @@ def _(): ) +def test_generate_is_idempotent(pytester): + """Test that invoking the generation script twice yields the same output. + + Also make sure that applying the generated code twice to the same module + does not duplicate tests, since scenarios() skips already bound scenarios. + """ + pytester.makefile( + ".feature", + idempotency=textwrap.dedent( + """\ + Feature: Idempotent code generation + + Scenario: First scenario + Given I have a step + + Scenario: Second scenario + Given I have a step + """ + ), + ) + + first = pytester.run("pytest-bdd", "generate", "idempotency.feature") + second = pytester.run("pytest-bdd", "generate", "idempotency.feature") + assert str(first.stdout) == str(second.stdout) + + pytester.makepyfile(str(first.stdout) + "\n\nscenarios('idempotency.feature')\n") + result = pytester.runpytest("--collect-only", "-q") + collected = [line for line in result.stdout.lines if "::test_" in line] + assert len(collected) == 2 + + def test_unicode_characters(pytester, monkeypatch): """Test generating code with unicode characters. @@ -184,15 +209,12 @@ def test_unicode_characters(pytester, monkeypatch): from pytest_bdd import ( given, - scenario, + scenarios, then, when, ) - - @scenario('unicode_characters.feature', 'Calculating the circumference of a circle') - def test_calculating_the_circumference_of_a_circle(): - """Calculating the circumference of a circle.""" + scenarios('unicode_characters.feature') @given('We have a circle')