cucumber / cucumber-jvm

Cucumber for the JVM
https://cucumber.io
MIT License
2.7k stars 2.02k forks source link

Cucumber matches more than one step definition #2864

Closed Darenth closed 6 months ago

Darenth commented 6 months ago

I faced the next situation with cucumber 7. There are 2 steps in the feature file:

And I select <option1> and <option2>
And I select <option1>

And when I run it I've got: io.cucumber.core.runner.AmbiguousStepDefinitionsException matches more than one step definition: I think Cucumber has to recognize both staps are independent (different) because the text is not the same and they have even different amounts of arguments. Im using: Java 11, Cucumber 7.14.0-7.16.0 (previous versions didn't tested) Maven 3.9.8

There is repo for reproducing: https://github.com/Darenth/CucumberWithProblem

Scenario Outline: select options And I select and And I select

Examples:
  | option1 | option2 |
  | LIGHT   | DARK    |

-->

image

mpkorstanje commented 6 months ago

Scenario outlines are syntactic sugar, and the the values from the examples table do not have to be related to arguments in your step definition. For example your scenario outline with a single example:

Feature: Belly

  Scenario Outline: select options
    And I select <option1> and <option2>
    And I select <option1>

    Examples:
      | option1 | option2 |
      | LIGHT   | DARK    |

is equivalent to writing:

Feature: Belly

  Scenario: select options
    And I select LIGHT and DARK
    And I select LIGHT

As you can see these are merely steps with nothing to distinguish which parts are arguments and which parts are just part of the text.

And your step definitions are:

    @And("I select {} and {}")
    public void iSelectOptionAndOption(Flex flex, Flex Flex) {

    }

    @And("I select {}")
    public void iSelectOption(Flex flex) {
    }

And these correspond to the regular expressions: ^I select (.*) and (.*)$ and ^I select (.*)$respectively. Not only do these match both steps, the second regex will match everything the first matches. And that is a fairly fundamental limitation of regular expressions that can not be solved without adding more information about the parameters that should be matched.

The way to do this would define a parameter type that specifies

    @ParameterType("LIGHT|DARK")
    public Flex flex(String value) {
        return Flex.valueOf(value);
    }

    @And("I select {flex} and {flex}")
    public void iSelectOptionAndOption(Flex flex, Flex Flex) {

    }    

    @And("I select {flex}")
    public void iSelectOption(Flex flex) {
    }    

And these correspond to the regexes: ^I select (LIGHT|DARK) and (LIGHT|DARK)$ and ^I select (LIGHT|DARK)$respectively. Which solves the problem where second regex would match everything the first does.

Darenth commented 6 months ago

Thanks a lot, @mpkorstanje for the detailed explanation! Your solution help me