-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Deprecate "calling steps from step definitions" functionality #1362
Comments
You mean deprecating the One that comes up frequently for me is the notion of scoping output: Then 'I should see "$text"' do |text|
expect(page).to have_text text
end
Then 'I should see a cat GIF' do
# code for locating cat GIF element
end
Then /^(.+) within (.+)$/ do |step_text, region_name|
within selector_for(region_name) do
step(step_text)
end
end ...so that I can do |
Use ruby's own Use Helpers or Classes for specific pieces of reusable code Use case / if logic to decide based on what text which class / helper to instantiate and use |
I’m quite familiar with both these techniques, but I don’t see how either would be useful in the case I described. Do you?
The Gherkin parser already does pretty much that, and I would rather reuse it than reimplement it. This idea seems like a step backwards to me. If you need the problem stated in a more generic form: I have a modifier that I would like to be able to apply to any arbitrary Gherkin step from within the scenario. I only see one maintainable way to do that, and it’s this: Then /^(.+) with modifier$/ do |step_text|
with_modifier { step step_text }
end How else would you propose to implement this, without maintaining a separate table of step definitions? I don’t understand how I can do this if |
That's a primitive one that would allow you to call them. If they were module functions mixed into the World. Also I would advocate having one "mega-step", that does 10 different things, and actually have 10 small steps, even if portions are re-used. It makes debugging easier. And in-fact most of our open source stuff we're un-coupling steps, i.e. removing the areas that do 2 different things for 2 different steps that do 1 thing. for your example you could do.
|
@luke-hill No, that wouldn’t work at all. If you look again at my example, you’ll notice that ...and if you are, then once again we need to map from Gherkin text to step definition, and so we’re right back to needing the Or did I miss something? I’d love to better understand what you had in mind, but so far it doesn’t seem like a suitable solution. |
I don't want to go into exactly how to deal with your situation line by line, but you simply need a 1 to 1 mapping between what you capture and what you send. It would work, because you're assuming you have to send a step which has spaces in, but you could sanitize that. There are many things you would perhaps need to do, unique to your situation. Your Essentially the methodology you have of having a "master step" which then delegates to a multitude of other steps is something I would disuade in most circumstances, it's too hard to triage, too taxing for newcomers and relies heavily on almost a bus-factor style approach where a few siloed team members know everything. It's currently an area in aruba we are "un-DRYing" if you like to think of it that way. See cucumber/aruba#666 perhaps for a code explanation? EDIT: This has massively gone off tangent from the original placeholder, which is to deprecate the usage of the |
I’ll admit I was sort of hoping for that. I’m very experienced with both Cucumber and Ruby, but I can’t figure out how to implement your suggestions in a way that makes sense. However, if that’s more appropriate for the mailing list or something, we can take it there.
Correct. And that’s what the
Are you seriously proposing that? The Gherkin step definitions already provide a perfectly suitable translation. A massive In other words: I already know how I want to translate that capture. I want to translate it the exact same way that Gherkin would if it were a step by itself. That’s why I think I need
As far as I can tell, this is exactly on topic for that. The whole point of my comments here is that we shouldn’t deprecate those methods, because they make possible some very useful abstractions that AFAIK can’t be implemented in any other way. I hope I’ve explained my use case pretty clearly here, but all I’ve gotten is rather glib generic answers that won’t actually work in the situation I described as far as I can tell. I find this extremely frustrating. If the core team is bound and determined to deprecate
Then if another workable solution is not provided, I’ll either have to abandon Cucumber at that time (which would be a huge pity) or maintain my own fork or plugin (which I really don’t want to put the time into doing). |
Everyone who works on cucumber are volunteers. Whilst there is a small amount of funding available for fixing and tracking issues, looking for custom solutions is unlikely without a serious injection of funds. There is an OpenCollective account visible here: https://opencollective.com/cucumber In short, the 2 key takeaway points here are you can re-abstract your use cases in a language specific way see cucumber/cucumber-js#1232 for more info (Which I see you've commented on). Also that in order to maintain this abstraction, it provides the user with no discernable benefits, and just a mountain of issues. Using a language specific abstraction would only provide benefits, and no drawbacks. Now as to your specific use case. I can't comment on it specifically, but if I was to hazard a guess, you may be in a situation similar to what I had at a previous company and/or what we have in aruba ( Refactoring a large app is something which is an arduous task I admit, but keeping it as a monolith has issues. Here as I see it you have a simple choice (Both are valid options)
Again both are valid. However with the first case, you gain a (arguable), advantage that you can use the latest versions. In the same way that maintaining a Windows95 OS is viable, but don't then expect to be able to run NVidia Turing technology with 16-AA (Not sure if you get this reference, apologies if not). If you are very experienced with Ruby, then you should know that using language specific abstractions, such as Helpers, Classes and more indepth stuff such as Singletons or Anonymous classes, all come with large stacktraces and good debugging tools. I know (From reasonable personal experience), that using We are not advocating for one minute that our way is the only way or the highway. What we are advocating is we believe this is the way the software "should" work. People are completely within their rights to disagree, however, we would like users to attempt to use workarounds where explained, or try to understand the logic behind our decision-making. In this instance, the logic is simple, deprecate something which is old, and not best served in Ruby (It has already been removed from other languages). You can use a 1-1 mapping as I've mentioned to solve your issue. But short of doing the work for you (Which I'm sure you'd expect, would be perhaps crossing a line), you need to perhaps spike a few different solutions for yourself. If you want further reading, check a lot of the aruba library between around I hope this has explained things better. But if not I suggest doing some reading into some of the points, checking out the code areas I've suggested or maybe (Ability in other languages not known), checking Java or other versions of cucumber to see how other people have conquered this issue (It's not just a ruby issue) |
I’m aware of this, of course. And thanks for your time and energy!
Nope. I’m in exactly the situation I said I was in: I have one place in the codebase where I apparently need to use I have a feeling we’re talking past each other in this regard, because you keep suggesting solutions that are not relevant to my use case, and you have made several guesses about my use case that have nothing to do with anything I’ve said about it. I’ll be happy to clear up any further confusion about what I’m trying to do, but I don’t know how else to say what I’ve already said.
I’m aware of that. It is not relevant to my question, because (1) I have never found debugging these steps to be a problem and (2) as far as I can tell, those abstractions do not provide me what I need here.
That’s not what I’m doing. That’s not something I’d ever do. Please read again my description of the technique I am using. I am making one call to
From my perspective, that sure does seem like what is going on here. You (the core team, not you personally) are trying to remove something that works well and doesn’t have a good alternative, because it seems like it “shouldn’t” be in there.
I’m not asking you to do my work for me. But: when you deprecate a feature that people have come to rely on, the onus is on you to suggest an alternative approach that actually replaces the deprecated feature. Certainly I’d do that if anyone asked a similar question about a deprecation in any of the libraries I maintain. The crux of my problem is that I want to do the following:
1 and 3 are trivial. I cannot see how to do 2 without |
|
I'm sad to see this go away, for the same reason as @marnen has outlined.
As @marnen has already attempted to explain - this is what we're already doing with Cucumber. You seem to be proposing we implement another mapping between natural text and code, mimicking that we already have (with Given / When / Then + Regexp), which seems so non-sensical to me. I can appreciate the argument that this feature is hard to maintain, but you're not succeeding in proposing an alternative solution. |
What you currently do is the following.
What I am proposing is this
Given I'm a healthy contributor, I'm aware this sounds bad, but you need to trust me that the new methodology is better. I'm speaking as someone who had at a previously company a healthy amount of Also I've attached a rudimentary code example. Previously
Now
EDIT: There are also about 3 or 4 other ways of doing it, if you don't want to mix in the method to the global NS Obviously I've made up a long namespace to illustrate it could be whatever. I'm not keen on this backwards and forwards motion because it's all theoretical, could you maybe have a go at doing it, or perhaps illustrate with a git repo why it wouldn't work. Sometimes in learning (Using something called the VAK model), people are not understanding one particular way of explaining, so maybe an alternative is in order? |
No, we don’t need to trust you. You need to tell us why it is better. Right now, it seems like extra work for no benefit, at least for our use case.
That has not been my experience with my
As @badeball and I have said, it wouldn’t work because it duplicates the mapping that Cucumber already provides, and because it’s not general. That is, with your Again, my canonical case is the one I already described, that of |
I think I've landed on the crux of the issue. In that you're looking to do the exact thing we're looking to discourage here. So my earlier comments that were ignored I now realise were done either accidentally or because you disagree with them (Which is fine). So just to reiterate (This will be for the last time, because we're going in circles). You're advocating the usage of something that is being deprecated. The reason it is being deprecated is as aslak has previously mentioned and is linked to in a couple of articles (As well as the notional lack of it now in all other major cucumber flavours). If you wish to continue writing 1 'mega-step' this is not too dissimilar to my original POV which was that I had "worked at a company with 1 step that called 5 steps", because in essence you have something similar to that in your codebase, just a bit more varied (Steps that can either perform actions or assert instead of steps that combine other steps which do actions). aslak also put a quite clean JS code-snippet, and I've put a reasonably concise ruby snippet. There could be other ways you could do it. As for the original comment, kinda rude tbh. If you don't want to agree with me that's fine. But you can't (or shouldn't), half cut and paste things out of context. I explained why the new technology was better, by alluding to stacktraces, as well as the fact it uses the MRI under the hood, instead of having cucumber replicate that. One alternative way (I'm losing track of how many variants to give you), is perhaps to think of it as JRuby. It does stuff that isn't normal for Ruby and regular ruby doesn't support. So they decided to make a branch offshoot. In theory (Although I wouldn't advise this), you could make a new gem that allowed this behaviour, monkeypatched and swallowed all warnings, and overrode the behaviour in cucumber-ruby v4. However I would advise against this as it wouldn't be future-proof. Also I've seen across a few of your posts a reference to "your" use case, which is all well and good, but cucumber is currently the number 1 used BDD tool across tech teams world-wide - with just a few of the users here: https://cucumber.io/ Some of these companies (Such as CodeFirst), https://opencollective.com/codefirst actively contribute to the maintenance of cucumber, and as such if they had a request it could perhaps be prioritised dependent on needs e.t.c. If you can think of a way that this behaviour can be maintained either in this gem or an offshoot gem, without going against the tenets of what we've explained, then please feel free to do so. I'm not going to comment on this any more, because I feel I've tried in a few different ways to explain in quite good detail what to do. I have an extensive background in education but sometimes I'm not able to explain something to a particular group/class/person. I would therefore point you to some of the links I've used before
There are some good resources as well about BDD being the living documentation. @sebrose probably has a wealth more links he could possibly share. But from your use cases and the direction you've been moving in, I'm not sure I'm the best person to help, as it seems as if the reason for you wanting to code in a specific way without using a better methodology may be better answered with a holistic company-based query (Which Seb is much more qualified and experienced than I) |
OK. You’ve seen my use case now. What would you advise doing for that use case (that is, where the argument to To reiterate, what I think I need is a general step modifier mechanism which has the following properties:
I’d love to know how you’d approach that. Right now, as I see it, you’ve talked all around the problem and not given me a usable solution. In other words, if you’re trying to discourage it, please give me a concrete suggestion as to what to do instead.
I don’t think that’s what I’m doing. Of course
As far as I can tell, none of your snippets help me figure out how to implement within the constraints I gave without using
Yes, but unfortunately that’s not better in any way that I care about. What I care about most is being able to call existing steps (with modifiers) using the same mappings that Cucumber already does. To put it in your terms, I want to use the existing Gherkin mappings under the hood, instead of having MRI replicate that.
If
I understand that we’re both getting a bit frustrated here, but at this point I’m no wiser than when I started this discussion as to how to achieve my goal without
I’ll look at those, though I’m skeptical of anything that advocates unDRYing. :)
These are probably great for replacing other use cases of
I’d be happy to use a better methodology if I could only figure out how to get it to work for what I want to accomplish. If you can’t help me, I hope @sebrose or someone else can. |
@marnen If the removal of a single feature from a tool, a feature which has been deemed an anti-pattern almost as long as it has been around, is enough to make you stop using the tool entirely, then you might want to reconsider why you are using the tool in the first place. |
@enkessler I believe I’m using this feature in a way that is not actually representative of the antipatterns that it’s often associated with. I don’t believe I’m using Cucumber for the wrong reasons, but the fact remains that, in a very narrow set of circumstances, I rely on this feature. If the feature goes away, I won’t want to stop using Cucumber, but I may well have no choice, if at that point Cucumber will no longer be adequate for my purposes. Or so I think. If you think that my use of |
I should mention for completeness' sake that I've been considering an option that turns the logic inside out: Then /^I should see "(.+?)"(?: within "(.+?)")?$/ do |text, selector|
with_selector(selector) do |page|
expect(page).to have_text text
end
end
Then /^I should see a cat GIF(?: within "(.+?)")?$/ do |selector|
with_selector(selector) do |page|
# code for locating cat GIF element
end
end
private
def with_selector(selector, &block)
within(selector || 'html', &block) # or something like that
end ...but I really don't like it: it requires rewriting every step that I ever use with the modifier, and has other maintainability issues as well. So far this is the best way I've found around using |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week if no further activity occurs. |
I've had the feeling that this should be pulled out into a plugin for a while now. It would also be my preferred method to deprecate.
|
@tooky That sounds reasonable to me if it must be removed from core. |
@tooky Couldn't Cucumber stay in the 4.x version as long as the new plug-in was automatically used by Cucumber? It's not really breaking behavior until the user has to start pulling in the additional library themselves. |
Yes I think that’s true. My thought was I’d rather not suddenly get deprecation warnings on a minor or patch upgrade.
…On 15 Jun 2020, at 11:20, Eric Kessler ***@***.******@***.***>> wrote:
@tooky Couldn't Cucumber stay in the 4.x version as long as the new plug-in was automatically used by Cucumber? It's not really breaking behavior until the user has to start pulling in the additional library themselves.
|
@marnen thanks for patiently explaining your use-case. Having read this thread, I was about to suggest something along these lines when I saw that you already had:
Can you unpack those objections for me a bit more? Why do the existing steps need to be re-written? What are the other maintainability issues? |
Because using this method, the Contrast this with what I currently do, where I write the Does that answer your question? Do you need this explained further? |
I hear ya @marnen. The thing we're trying to avoid is where people try and evaluate Gherkin at runtime, like:
Instead of just extracting/calling a helper method:
But I can see how you've made use of this feature to give you something powerful. I wonder if we could do something that helped us to avoid the dynamic runtime Gherkin evaluation we're trying to avoid, but still have you this kind of functionality, something like:
That new WDYT? |
@mattwynne Thanks for the suggestion! I think your I have a lingering fear that it’s too rigid, and I’m not sure I like the idea that it would work by creating lots of extra step definitions—couldn’t that cause a combinatorial explosion?—but it would certainly work for my use case. |
@mattwynne Ooh, I really love the macros idea. It seems like it would keep both reusability and readable Gherkin. |
Summary
Calling steps from step definitions is one of the features I regret having added to Cucumber. It's like a poor man's implementation of subroutines (which all languages support natively) with the following drawbacks:
Using this functionality should print:
The XXX link should point to a page in the documentation explaining in more details how to use fiunctions/methods with Cucumber. It can be based on the explanation I gave in cucumber/cucumber-js#1232
The text was updated successfully, but these errors were encountered: