Skip to content
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

Fix parsers.re doing only partial string match #539

Merged
merged 3 commits into from
Jul 15, 2022
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
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Unreleased
----------
- Fix bug where steps without parsers would take precedence over steps with parsers. `#534 <https://github.com/pytest-dev/pytest-bdd/pull/534>`_
- Step functions can now be decorated multiple times with @given, @when, @then. Previously every decorator would override ``converters`` and ``target_fixture`` every at every application. `#534 <https://github.com/pytest-dev/pytest-bdd/pull/534>`_ `#525 <https://github.com/pytest-dev/pytest-bdd/issues/525>`_
- ``parsers.re`` now does a `fullmatch <https://docs.python.org/3/library/re.html#re.fullmatch>`_ instead of a partial match. This is to make it work just like the other parsers, since they don't ignore non-matching characters at the end of the string. `#539 <https://github.com/pytest-dev/pytest-bdd/pull/539>`_

6.0.1
-----
Expand Down
4 changes: 2 additions & 2 deletions pytest_bdd/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ def parse_arguments(self, name: str) -> dict[str, str] | None:
:return: `dict` of step arguments
"""
match = self.regex.match(name)
match = self.regex.fullmatch(name)
if match is None:
return None
return match.groupdict()

def is_matching(self, name: str) -> bool:
"""Match given name with the step name."""
return bool(self.regex.match(name))
return bool(self.regex.fullmatch(name))


class parse(StepParser):
Expand Down
55 changes: 54 additions & 1 deletion tests/args/regex/test_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_every_steps_takes_param_with_the_same_name(testdir):
When I pay 2 Euro
And I pay 1 Euro
Then I should have 0 Euro
And I should have 999999 Euro # In my dream...
And I should have 999999 Euro
"""
),
Expand Down Expand Up @@ -55,6 +55,59 @@ def i_should_have(euro, values):
result.assert_outcomes(passed=1)


def test_exact_match(testdir):
"""Test that parsers.re does an exact match (fullmatch) of the whole string.
This tests exists because in the past we only used re.match, which only finds a match at the beginning
of the string, so if there were any more characters not matching at the end, they were ignored"""

testdir.makefile(
".feature",
arguments=textwrap.dedent(
"""\
Feature: Step arguments
Scenario: Every step takes a parameter with the same name
Given I have 2 Euro
# Step that should not be found:
When I pay 1 Euro by mistake
Then I should have 1 Euro left
"""
),
)

testdir.makepyfile(
textwrap.dedent(
r"""
import pytest
from pytest_bdd import parsers, given, when, then, scenarios
scenarios("arguments.feature")
@given(parsers.re(r"I have (?P<amount>\d+) Euro"), converters={"amount": int}, target_fixture="wallet")
def _(amount):
return {"EUR": amount}
# Purposefully using a re that will not match the step "When I pay 1 Euro and 50 cents"
@when(parsers.re(r"I pay (?P<amount>\d+) Euro"), converters={"amount": int})
def _(amount, wallet):
wallet["EUR"] -= amount
@then(parsers.re(r"I should have (?P<amount>\d+) Euro left"), converters={"amount": int})
def _(amount, wallet):
assert wallet["EUR"] == amount
"""
)
)
result = testdir.runpytest()
result.assert_outcomes(failed=1)
result.stdout.fnmatch_lines(
'*StepDefinitionNotFoundError: Step definition is not found: When "I pay 1 Euro by mistake"*'
)


def test_argument_in_when(testdir):
testdir.makefile(
".feature",
Expand Down