Cucumber expressions in StepArgumentTransformation expression #986
Replies: 4 comments 25 replies
-
|
Do I understand right, that basically what you would like to have is to be "able to use Cucumber expressions for step argument transformations, with the ability to use custom parameter references in them that are resolved by another step argument transformations", correct? |
Beta Was this translation helpful? Give feedback.
-
|
I would like to add that I think I've encountered this scenario on every BDD project I've worked on, usually for dates: Given a thing happened yesterday
Given a thing happened 5 minutes ago
Given a thing happened 9 hours and 30 minutes agoConceptually we're looking at one step which accepts a relative expression of a point in time: we want a I typically have worked to the platform's strengths and produced a step definition for every variation, if for no other reason than to get the auto-complete suggestions available for my team to follow. The steps revert to taking primitives and we do the conversion within the step itself, not an argument transform. [Given("a thing happened {int} days and {int} hours ago")]
public void GivenAThingHappenedDaysAndHoursAgo(int days, int hours)
{
...
}It means I have to produce a bunch of steps that I'd rather not have to write, and it explodes if there are multiple date-related conceptual steps that all need variations but they are simple at least. |
Beta Was this translation helpful? Give feedback.
-
|
Sure.
Imagine you want to parse the string "A + B * C", but elsewhere you have "B
* C + A". Still elsewhere you have "B * (A + C)" and "(A+B)*C". And so on.
If you match the patterns "(\w+)\s*\+\s*(.*)" and "(\w+)\s*\*\s*(\w+)", you
get lucky on the first one. To bring in the second string, you have to get
much more complex. And so on.
Furthermore the first pattern also matches strings like "A+ students are
the best students", then gives a poor explanation for why the second half
of the string can't be converted to whatever those expressions are supposed
to mean.
If I push the problem down to ANTLR and just take expressions as (.*), then
I end up matching and kind of grammatical error and getting it highlighted
as a valid expression, only finding out it's not when I run the tests and
get a stack trace.
With the ability to make Reqnroll aware of "minor grammars", I don't have
any of those problems. I can express each relationship appropriately. I can
use precedence to prefer the right one. Most importantly, I don't have to
accept false matches as part of the cost of doing business.
Also there is the issue that right now the mappings from one STA to another
are by type and not name, creating an asymmetry between how parsing works
at the expression and argument level. That can go away, too.
…-- Max
blog.abiding.software
On Thu, Jan 29, 2026, 2:59 AM Gáspár Nagy ***@***.***> wrote:
OK. So basically you would like to have an embedded DSL into the Cucumber
expressions. That makes sense.
What I would like to analyze further is that your expectation of "giving
Reqnroll's execution and IDE engines a complete, perfect understanding of
the structure so that two possible interpretations [...]" mean. Let's try
to find a concrete example of two possible interpretations and let's
compare the expected behavior versus the behavior you have with (.*)
right now. Do you have such an example that we could look at?
—
Reply to this email directly, view it on GitHub
<#986 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AC4DRFTHRZR6KQNP7U2C6JD4JHRYVAVCNFSM6AAAAACQ3RBREKVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNRTHEYDOMA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
|
Cucumber Expressions
Then: {StringFixture} = {StringFixture}
Then: {NumberFixture} = {NumberFixture}
Step Arguments
StringFixture: string (\[[\w\d_]\])
StringFixture: "((?:[^"]|"")*)"
StringFixture: (.*) \+ (.*)
NumberFixture: number (\[[\w\d_]\])
NumberFixture: (\d+)
NumberFixture: (.*) \+ (.*)
Now you either get overlap for the statement "Then string [x] + string [y]
= string [a] + string [b]" or you have to replicate the other expressions
in place of .*.
I run into this every time I build a project using something like Cucumber
or Reqnroll. Often, I give up and just train the stakeholders to read
Gherkin in C#. This time, I'm trying to break through.
…-- Max
On Mon, Feb 2, 2026, 2:56 AM Gáspár Nagy ***@***.***> wrote:
Thx, I still not get the example.
I would like to focus on full steps, because this is the only way we can
evaluate this in full context.
So let's say we have a .NET type called CustomExpression that we use to
represent expressions like A + B * C or (A+B)*C.
Let's look at the step you have mentioned.
Given A+ students are the best students
In this step, "A+" is not an expression, but a grade, so the step
definition would be:
[Given("{Grade} students are the best students")]
public void StudentsAreTheBest(Grade grade){...}
This will only accept Grade as text before students are the best, so it
does not matter what step argument transformation you have configured for
CustomExpression, because Reqnroll will only consider the ones that are
related to the expected type (Grade in this case). So *there is no
collision* even if you register CustomExpression with (.*)
A more complicated example would be.
Given the performance of the students are calculated with formula A + B * C for the year
Here the problem could be whether "for" is part of the expression or the
step text.
However the logic of step matching in Reqnroll solves this.
Assuming you define your step definition and step arg conv as:
[Given("the performance of the students are calculated with formula {CustomExpression} for the year")]
public void GivenXXX(CustomExpression expression) { ... }
[StepArgumentTransformation("(.*)")]
public CustomExpression ConvertExpression(string expression) {
// use ANTLR or any custom parser to parse expression to CustomExpression
}
Again, *there is no collision*, because the final regex that Reqnroll
internally calculate will be ^the performance of the students are
calculated with formula (.*) for the year$ so it strips out A + B * C
from the text (and not the for!) and passes it to the step argument
transformation.
Do you have a full step example where you do see an actual problem with
the (.*) matching for custom embedded expressions?
—
Reply to this email directly, view it on GitHub
<#986 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AC4DRFS7HIPWHJTQPGSJCP34J4UMZAVCNFSM6AAAAACQ3RBREKVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNRXGEZTSNQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I tend to lean on sophisticated graphs of step argument transformations.
Consider the following line:
Given line item [I] is per quantity [Q] of unit [U]At the statement level, this is easy to automate with an expression like
Given {LineItem} is per {Amount}The problem comes when I want to automate the step argument transformation for the amount fixture.
I have many different ways that I can express a quantity. I would like all of them to apply in the "quantity of unit" pattern. I have fewer ways to express a unit, but I would still like them all to apply.
To accomplish this right now, I either have to write a regex that captures all the possible shapes of {Quantity} or settle for something like
(.*).The former is unsustainable because I must duplicate and maintain the graph of possible expressions. For large, complicated step arguments, this is odious.
The latter casts a wide enough net that it frequently confuses the highlighter and sometimes confuses the actual engine. I often find myself doing this, living with the red underlines, and cleverly disambiguating when actual overlap impacts the test run (admittedly infrequently).
It would be much easier and cleaner if I could write a step argument transformation with the pattern
{Quantity} of {Unit}and then get all the parsing and pattern-generation logic that applies at the step level for free.If this is something that is already implemented, my apologies, I cannot find it.
If this is something that could easily be done as a plugin, I will happily write it.
If this is something that involves a core change, (which I suspect it is), I'll gladly generate the PR.
...but first it seems worth talking about.
Beta Was this translation helpful? Give feedback.
All reactions