-
Notifications
You must be signed in to change notification settings - Fork 92
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
Improve title handling, especially for example scenarios #271
base: master
Are you sure you want to change the base?
Improve title handling, especially for example scenarios #271
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the slow review, been a busy week.
Urgh, I hadn't appreciated how complex & nested the interfaces for these classes are. Considering that, I'd be nervous about adding yet another.
As I see it, the problem we're fighting is:
- Arguably,
getTitle()
has always returned the wrong thing for an ExampleNode - the idea implied by the interface is it looks and behaves like a Scenario, in which case thetitle
should be the user-supplied title of the Scenario Outline, not the example values in text form. - However, we can't change the result of
getTitle()
because there are existing valid uses - in Behat and probably elsewhere - that need the text example and rely on getting it from that method. - And these are implementations of interfaces, not simple DTOs, so as you say, there's no easy way to provide a BC path to deprecate ambiguous method and add a new one.
So I think my suggestion for now is:
- Add the index to
ExampleNode
as planned. - Add
getName()
toExampleNode
(instead of calling itgetScenarioTitle()
) - this would align with the naming in cucumber's AST. - Add the
getExampleText()
as planned. - Deprecate
getTitle()
only onExampleNode
but only with a docblock tag pointing togetName()
orgetExampleText()
, not a runtime deprecation. - Open an issue for a future major version to consider changing the interfaces so that anything "scenario-like" has a
getName()
instead of agetTitle()
. I would probably do this alongside bumping Behat's major, so that we can be sure any extensions etc only get the Gherkin objects they were expecting even if they don't specify abehat/gherkin
constraint of their own.
This means that:
- The change is BC for all existing users
- Only users who are writing formatters or similar and explicitly considering ExampleNode will see the deprecation (in their IDE or e.g. phpstan etc). But these are really the only cases at the moment where it's necessary to encourage people to be explicit about which one they want.
- If / when we release a new major renaming
getTitle()
togetName()
then code that has already been updated forExampleNode
will be fine, anything that is only using the scenario interfaces will by default get the new (/expected) value instead of the table text, and we can highlight the difference in the upgrade notes.
This feels a bit messy, and I'm not sure it's right, but I think it probably is the least-worst compromise solution to adding this feature in a BC way.
Thoughts welcome!
@acoulton your points are most fine to me except this:
Why wait? If we do that later, we won't have a chance to deprecate it - it will suddenly be renamed from getTitle to getName (unless we have to wait for 2 major versions). I would define getName in both scenario and example nodes. Additionally, I would consider creating a new The point is that it would nice (for users) to code against Another interesting thing to note: I was considering deprecating getTitle at the interface level (it is defined in |
@uuf6429 my reluctance to introduce new interfaces/deprecate things at interface level at this stage is exactly because I think it needs more thought about what that structure should be. ISTR there are nodes currently defining I think part of the problem is methods have been defined at the wrong level of the hierarchy because they appear to be similar. For example You can see from the existing Behat formatter implementations that actually it very rarely makes sense to call But to deprecate it on the interface now, we need to find a well-designed and BC way to replace it across all the nodes that implement it : even though some nodes might actually be fine with Really for this usecase what we want to capture is that So if we had to introduce an interface now I would call it I wouldn't use |
Oh and on this specific:
I don't see us going immediately from this to a new major. Indeed with current resources a new major could still be a way off, especially if we're going to look at switching parser/moving to Pickles etc. And even when we release a major there will be a period where we are maintaining both version series. So there will still be an opportunity to deprecate methods between now and then - I just think we should minimise that until we can give users a clear overall explanation of what will be changing. Deprecating the method on ExampleNode where it is currently ambiguous feels fine to do right now as there is an identified problem with that specific object and we have an easy solution. |
@acoulton I agree with all points.
That makes sense to me, especially since most probably we'd want consistency over those two classes. What do you think about Anyway, I'll implement this interface and open this PR for review, I think we're in a good state now for that. |
I think Outline is a higher level grouping of scenarios, like a feature or a suite (or a Rule if/when we support them). It's not the same as a Scenario/Example which represents a specific set of executable steps. So I would leave Outline alone, for the moment at least. |
@acoulton side-note: there is a |
@acoulton while adjusting tests, I found out that outline represents the "scenario outline" node and that, for example, iterating over scenarios in a feature file will actually yield instance of that object. Apparently it is up to caller to iterate over the "example [child] nodes" of that object while ignoring the rest of the object. In short, a naive |
Fixes #270
Remaining points
getTitle
is defined inKeywordNodeInterface
- there's alsoScenarioLikeInterface
andScenarioInterface
).We could add a new interface on top, though - no BC break there.
getTitle()
togetScenarioTitle()
getName()
.\Behat\Gherkin\Node\ExampleNode::replaceTextTokens
public? It feels less important to do so with this PR, plus it's a BC break (sinceExampleNode
is not final)