From 7eaa3fc2c04d681913e00cc1901b378145df9737 Mon Sep 17 00:00:00 2001 From: Alessio Bogon <778703+youtux@users.noreply.github.com> Date: Sat, 5 Nov 2022 15:14:03 +0100 Subject: [PATCH] Re-add "`parsers.re` now does fullmatch."" This reverts commit 1d84d291d9a9feb0ccaa7a2f539ab5fbd2d24c4a. --- CHANGES.rst | 1 + src/pytest_bdd/parsers.py | 4 +-- tests/args/regex/test_args.py | 53 +++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3723006..131eb5f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,7 @@ Unreleased ---------- - Fix bug where steps without parsers would take precedence over steps with parsers. `#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 `_ `#544 `_ `#525 `_ +- ``parsers.re`` now does a `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 `_ - Require pytest>=6.2 `#534 `_ - Using modern way to specify hook options to avoid deprecation warnings with pytest >=7.2. - Add generic ``step`` decorator that will be used for all kind of steps `#548 `_ diff --git a/src/pytest_bdd/parsers.py b/src/pytest_bdd/parsers.py index 77e3e1a..0ae1670 100644 --- a/src/pytest_bdd/parsers.py +++ b/src/pytest_bdd/parsers.py @@ -42,14 +42,14 @@ class re(StepParser): :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): diff --git a/tests/args/regex/test_args.py b/tests/args/regex/test_args.py index ac1fe42..cce2d59 100644 --- a/tests/args/regex/test_args.py +++ b/tests/args/regex/test_args.py @@ -55,6 +55,59 @@ def test_every_steps_takes_param_with_the_same_name(pytester): result.assert_outcomes(passed=1) +def test_exact_match(pytester): + """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""" + + pytester.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 + """ + ), + ) + + pytester.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\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\d+) Euro"), converters={"amount": int}) + def _(amount, wallet): + wallet["EUR"] -= amount + + + @then(parsers.re(r"I should have (?P\d+) Euro left"), converters={"amount": int}) + def _(amount, wallet): + assert wallet["EUR"] == amount + + """ + ) + ) + result = pytester.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(pytester): pytester.makefile( ".feature",