-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Allow multiple step patterns on the same method #957
Comments
Plus 1! |
I think this feature would be nice to have too! Regarding the implementation I'd rather let the annotations be Your solution might be friendlier to non-Java 8 users, though. |
@moreau-nicolas it would be possible to make this annotation repeatable on JVM versions that support it. You can have it so that people using Java 8 can write:
And people using Java < 8 could write:
Notice that both examples work in Java 8, but only the latter works in Java < 8. |
I don't believe this is a feature in any of the other Cucumber implementations. You are able to delegate to a method call, having two annotated methods, 3 methods total. Help me understand how will this feature improve the quality of your Scenarios? How will this feature improve the communication between stakeholders? |
Cucumber relies on the natural language capabilities of Gherkin to allow tests to be written that are supposedly easy for both managers and programmers to understand. However, it's not always one person that writes specs (Gherkin .feature files) and even if there is one person it's very easy to write two steps that mean the same thing in syntactically different ways. Currently, the only ways to handle syntactically different but semantically the same steps are:
Since Cucumber and Gherkin pride themselves on natural language and quality of the human-computer interface, I believe there should be an easier way than delegation to allow both syntactically different steps to exist. |
I completely agree with @mikeholler ! |
+1 - as the number of scenarios we have grows and grows - 240 and counting in one of our products - it's very possible that you end up reusing steps whose function is exactly what you want, but as part of the plain-language story you are describing with scenarios and steps, sounds a little weird. I've ended up using the regex option for is/are, a/an, plurals, etc., which works, but does make it look a little confusing to someone else who is coming up to speed on our integration tests; I'd hate to extend it much farther than it already is. It would be handy to put another @when or similar annotation on a glue function instead. I've just accepted that occasional lines in my scenario will work functionally but only "mostly" make sense from a language perspective. |
@jcannon-sovrn I didn't think about it at first, but pluralization is an obvious benefit to this. @dkowis this is something I would love to see added (obviously) but am wondering if Cucumber-JVM is the best place to ask for this feature. Is there a better place to make this request, perhaps on a universal cucumber standard rather than this implementation? |
+1 for this. I've some situations that means same code but diff step def. I'd love this feature. |
@dkowis any update on whether this is the correct place for this issue? |
@mikeholler have you found out anything regarding this issue, a workarround ? |
The pure java implementation stands alone using annotations. Even the scala and jruby backends don't use annotations and instead use a DSL. Ruby and Javascript implementations also use a DSL. And it is limited to one regexp per step method, or an ugly complicated regex. I believe the proper solution in all of the other languages is to write methods that delegate to another method. Totaling N+1 methods for the number of regexes you want to match, or even just N methods if they delegate directly to the original. I think this is the right place for the issue, but I don't think it's something we want to have in cucumber-java. I think the solution is going to be:
|
@dkowis, thanks for responding. I respectfully disagree with your position on the basis of code (glue) quality and readability, and beg you to reconsider. It's hard to argue that delegating to an older step is more concise and readable than my proposed solution. It seems to me the point of "glue" functions is to link a step definition with instructions for step execution. That is, to map syntax to semantic meaning. The glue should be considered semantically (this is how to do X) rather than syntactically (do X). It's true that most other languages utilize a DSL, but even with a DSL the ability to have multiple syntactically different regexes defined for a single step is possible by allowing the DSL to accept an array of regexes in addition to a single regex. Perhaps this is a change that needs to happen on the Cucumber platform level, rather than the Cucumber-JVM level. Thank you for reconsidering. |
I always read Andrew Premdas/@diabolo posts on the cukes group very carefully. From what he write I know that he as tons of more experience with using Cucumber(-Ruby) than I have with using any Cucumber implementation, and I want to tap into that experience as much as I can. I common thread many of these post is to to focus on writing good helper classes to be able to interact with the system under test, and to write one line step definitions that delegate to a method on a helper class ("step definitions should only be used for translating natural language to calls"). When doing that there is no problem with having one step definition for each regex variant calling the same helper method. If that experience with Cucumber(-Ruby) and Ruby is valid also for Cucumber-JVM and the JVM-languages, then allowing multiple step patterns on the same method is an unwanted functionality as it encourages user to put functionality in step definitions instead of delegating to well design and implemented helper classes. Which a much more experience Cucumber user than me has learned is a mistake. |
I've often wanted this feature, and I think it would be useful. We kinda have this in Ruby, with the one line step definitions feature which allows you to map step match patterns to method names. @gasparnagy has added this to SpecFlow. I think anything that encourages people to express themselves fully in Gherkin is a good idea, and I see this feature as pulling things in that direction. I agree with what @brasmusson has said about keeping step definition methods short, but I don't think this feature is incompatible with that guideline. |
I agree with @mattwynne : let's keep step definition methods short and provide a means to avoid methods that are just there to allow for synonyms. |
Yeah, I very much agree with @brasmusson as well, but also believe too that this feature is not incompatible with short, concise step code. One should always strive to write short, concise code for steps and in all other situations. I want this feature not as a crutch so I can write bloated steps, but as a tool I can use to consolidate repetitive steps all using the same helper method into a single step with multiple annotations. |
I've been writing my step definitions exclusively using lambda step definitions for a while now and I find that both of the desires in this discussion can be satisfied. Lambda step definitions allow for short, concise step code while also consolidating repetitive steps. For example: @When("^I perform the calculation (\\d+) plus (\\d+)$")
@When("^I add the numbers (\\d+) and (\\d+)$")
void performXPlusY(int x, int y) {
// do the step
} When there just isn't enough going on in a new set of step definitions to justify the creation of a proper helper class I find that I would write this by as: public LambdaStepdefs() {
When("^I perform the calculation (\\d+) plus (\\d+)$", this::performXPlusY);
When("^I add the numbers (\\d+) and (\\d+)$", this::performXPlusY);
}
private void performXPlusY(int a, int b){
// do the step
} And once the step definition starts to grow in complexity I find that I extract a helper class and things end up looking like this. I don't think this can be any more concise. public LambdaStepdefs(Calculator calculator) {
When("^I perform the calculation (\\d+) plus (\\d+)$", calculator::performXPlusY);
When("^I add the numbers (\\d+) and (\\d+)$", calculator::performXPlusY);
} So is this discussion still relevant after two years? I believe that Android support for java 8 was lagging behind a bit in 2016. I don't have any experience with that. But if Android support is longer an issue I think we can close this ticket. |
Thanks for the comment! You're right, lambda step defs do solve this problem quite nicely. I would love to use this on Android, however yes, this issue is still relevant. Android support for Java 8 was announced in the past few months, but that is only for latest version of Android. As you may have heard, Android is often slowly updated, with manufacturers, carriers, and users taking a long time to upgrade. The rule of thumb is that whenever something is announced that is not available in the backwards compatible support library, we have to wait at least two years before we can use it, since that's the time it takes for the majority of Android users to adopt a new android version announced today. Even with the backwards compatibility of some language features from Java 8, I do not believe this syntax is available to me. We'll have to wait until full language support of Java 8 has reached an overwhelming majority of users in a few years. |
We're not going to fix this. I reckon that by the time we do get around to fix this Google will have added more support for java 8 and the old Android versions have been become deprecated. |
I would like to use the suggestion below by @mpkorstanje The code compiles for me but at run-time I get Changing the primitive public LambdaStepdefs() {
When("^I perform the calculation (\\d+) plus (\\d+)$", this::performXPlusY);
When("^I add the numbers (\\d+) and (\\d+)$", this::performXPlusY);
}
private void performXPlusY(int a, int b){
// do the step
} |
Eh. I should have added that method references aren't supported in 1.2.5 and are currently somewhat supported 2.0.0-SNAPSHOT (no primitives yet) and will hopefully be fully supported once 2.0.0 is done. |
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 -
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 Closes #937
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 Closes #937 Closes #1048
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 Closes #937 Closes #1048
To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 Closes #937 Closes #1048
What about String values ? I am mapping as below its throwing error as mentioned @mpkorstanje |
Wrong issue. Oops. |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
I've been working with Cucumber JVM for the past couple of months and have been loving it, but there's one feature I would love to see and I think others might as well.
Basically, I'd like to do something like this:
I know it's technically possible to do this via regex, but it's very ugly, uses extra capture groups, and allows for more than the two desired patterns:
What does everyone think about this? Is it something that would be easy enough to add?
The text was updated successfully, but these errors were encountered: