cucumber / language-service

Cucumber Language Service
MIT License
12 stars 21 forks source link

Pytest-BDD parse matcher step definitions #205

Open neskk opened 3 weeks ago

neskk commented 3 weeks ago

šŸ‘“ What did you see?

I've configured the cucumber plugin to look for step defs in the right paths, but it's not matching the step-defs.

    "cucumber.features": [
        "**/tests/**/*.feature",
    ],
    "cucumber.glue": [
        "**/step_defs/**/*.py"
    ],

It finds and parses the files but does not recognize the step-defs:

[Info  - 10:45:53 PM] * Found 7 feature file(s) in ["src/test/**/*.feature","features/**/*.feature","tests/**/*.feature","*specs*/**/*.feature","**/tests/**/*.feature"]
[Info  - 10:45:53 PM] * Found 74 steps in those feature files
[Info  - 10:45:54 PM] * Found 26 glue file(s) in ["**/step_defs/**/*.py"]
[Info  - 10:45:54 PM] * Found 0 parameter types in those glue files
[Info  - 10:45:54 PM] * Found 0 step definitions in those glue files
[Info  - 10:45:54 PM] * Built 52 suggestions for auto complete

Example step-def:

from pytest_bdd import given, parsers, then, when

@given(parsers.parse("a stub exists with:" + os.linesep + "{stub_str}"))
@when(parsers.parse("creating a stub with:" + os.linesep + "{stub_str}"))
def creating_stub_with(
    request: pytest.FixtureRequest,
    context: handler.DynamicValuesHandler,
    mock_server: MockServerClient,
    stub_str: str,
) -> Stub:
    ...

We implemented most of the step-defs like this, using pytest_bdd parsers. Is this "parsers" format compatible with the cucumber plugin?


Another question I have is in regards to external libraries with step definitions. Currently at my company we're using a shared library that implements the most common step-defs we use in our tests.

Will the vscode cucumber plugin also pick these definitions as well from imports?

Thank you!

I can try to setup a full example if needed.

āœ… What did you expect to see?

I expected step definitions to be recognized and be able to use the plugin tools.

šŸ“¦ Which tool/library version are you using?

cucumber plugin v1.10 vscode 1.88.1

šŸ”¬ How could we reproduce it?

No response

šŸ“š Any additional context?

No response

neskk commented 3 weeks ago

@kieran-ryan sorry to bother you again but my questions kinda remain unanswered. I'm also sorry I didn't create this issue in the right place, as you had suggested.

Thank you for your time.

kieran-ryan commented 3 weeks ago

No worries @neskk

From what I managed to understand, the step definitions syntax that utilizes pytest_bdd.parsers is not recognized, right?

Correct. As such have listed this issue as an 'enhancement' - will need to assess effort to support and get back to you

Any thoughts on the issue of importing step-definitions from a library? It should work out of the box?

Assuming you are referring to including step definitions stored in an external library into your repository either via a submodule or a package (such as through 'pip'). If so, it should be possible by configuring the Cucumber extension or language server to point to where those step definitions are stored in order to scan them e.g. referencing inside a virtual environment could look like "venv/lib/**/shared_steps/**/*.py"

kieran-ryan commented 3 weeks ago

@neskk, interesting to observe some of the design choices with pytest-bdd - I see some slight deviations with multi-lines steps compared to the gherkin parser.

With pytest-bdd:

Given a stub exists with:
  one
  two
When creating a stub with:
  | sample |

With the gherkin parser:

Given a stub exists with:
  """
  one
  two
  """
When creating a stub with:
  | sample |

With Behave, we would include only the first portion of the expression; and would access the table via the context.

@given("a stub exists with:")
def step_impl(context):
    for row in context.table:
        model.add_user(name=row['name'], department=row['department'])

This creates a challenge to handle it appropriately with pytest-bdd, as the table is included in the match itself. To support, will likely need to find a pattern that ignores the additional lines to match only the step portion.

Thinking about what would need to be matched:

  1. Is os.linestep required in your case or could \n be used instead? We scan rather than evaluate the code that's referenced. However we could ignore that portion of the expression.
  2. Can positional arguments be used with pytest-bdd instead of keyword arguments i.e. if we removed stub_str from the expression - but kept it within the function - would it still run? The reason I ask, we typically handle named parameters as a reference to a Cucumber Expressions Parameter Type - which I assume are unsupported with pytest-bdd; so it may lookup and fail; though leaving it empty will match anything. May not be important if we ignore this section for multiline steps; and we have to consider what's optimal to support pytest-bdd. The following for example would work with behave

    @given("an unnamed parameter {}")
    def step_impl(context, parameter):
        ...