Skip to content

Allowing both DocString and DataTable on a single step #2315

Open

Description

@kruczekrobert

🤔 What's the problem you're trying to solve?

Currently, a Gherkin step can have either a DocString or a DataTable, but not both.

This is limiting for cases where the two argument types represent different parts of the same action or assertion. For example, a DocString can naturally represent a request body, GraphQL query, SQL statement, template, or expected text, while a DataTable can represent structured parameters, metadata, expected rows, or assertions for that same step.

The current mutual exclusivity means downstream runners and libraries cannot model such steps directly, even though the AST/message model already has separate docString and dataTable fields.

I want to discuss whether this exclusivity is intentional and should remain, or whether Gherkin should allow one DocString and one DataTable on the same step.

✨ What's your proposed solution?

Allow a single step to contain both existing argument kinds:

  • zero or one DocString
  • zero or one DataTable

This is not a proposal for arbitrary multiple step arguments. It should not allow repeated DocStrings, repeated DataTables, or a general ordered stepArguments[] collection.

Example:

Given I send a request
  """
  { "name": "Bob" }
  """
  | status | message |
  | 201    | created |

⛏ Have you considered any alternatives or workarounds?

One workaround is to split the step into multiple steps. That works, but it can make a single logical action harder to read when the DocString and DataTable are both part of the same operation.

Another workaround is to encode the structured data inside the DocString, for example as JSON or YAML. That loses the readability and ergonomics of DataTables.

A third workaround is to encode the text payload inside a DataTable cell. That is usually worse for larger text blocks, request bodies, SQL, GraphQL, XML, Markdown, etc.

I do not think supporting multiple DocStrings or multiple DataTables is a good alternative. That would be a much broader feature requiring a real collection model, ordering rules, formatter behavior, runner API changes, and probably additional syntax to distinguish repeated arguments. The proposal here is intentionally narrower.

📚 Any additional context?

This came up from a PR that attempted to allow both a DocString and a DataTable on the same step. Feedback on that PR raised valid concerns about ordering, pretty formatting, runner APIs, and broader ecosystem impact.

I would like to use this issue to gather consensus before treating the behavior as settled.

In particular, feedback would be useful from maintainers and consumers of Cucumber-JS, Cucumber-JVM, Cucumber-Ruby, Reqnroll, godog, and other projects using the Gherkin parser.

Questions to discuss:

  • Should docString and dataTable remain mutually exclusive?
  • If both are allowed, should both syntactic orders be valid?
  • Should there be one canonical order only?
  • Should runners expose these by type/name rather than as positional arguments?
  • Would this create practical compatibility issues for existing formatters or runner APIs?

Activity

  1. kruczekrobert commented on May 17, 2026

    @kruczekrobert
    Author
  2. mpkorstanje commented on May 17, 2026

    @mpkorstanje
    Member

    Should runners expose these by type/name rather than as positional arguments?

    This would up to the specific Cucumber implementation. Not every language supports named arguments. As a consequence though, positional arguments are the lowest common denominator. Which requires an order of sorts.

    Would this create practical compatibility issues for existing formatters or runner APIs?

    This depends on on the chosen solution.

    If the number of arguments for a step is limited to 1 doc string and 1 data table we can probably add an index field to PickleDocString, PickleTable. That looks reasonably backwards compatible. Though older implementations consuming information produced by a newer Gherkin parser might fail or omit one of the arguments as they are mutually exclusive.

    Otherwise it would be a considerable breaking change as Step and PickleStep would need a list of step arguments.

  3. gasparnagy commented on May 19, 2026

    @gasparnagy
    Member

    I think the idea of having both docstring and a data table for a single step has no fundamental issues (I remember the first SpecFlow versions supported that before we moved to a shared parser). I don't see any technical issues that could not be solved in some ways.

    But obviously it is a kind of change that require some time until all downstream tooling (execution frameworks, IDEs, reporting tools, etc) will support it. With the Rule keyword in Gherkin 6 it took 2+ years until is was generally supported.

    So the question is rather whether the added value is high enough so that it compensates the efforts.

    For the concrete questions:

    If both are allowed, should both syntactic orders be valid?
    Should there be one canonical order only?

    I think the docstring + data table order looks good, I would like to see another concrete example for the other order.

    Should runners expose these by type/name rather than as positional arguments?

    As @mpkorstanje mentioned, we must keep the positional arguments and I would enforce the same order as they appear after the step. So in case of the example posted, the last two stepdef param must be string and data table.

    Would this create practical compatibility issues for existing formatters or runner APIs?

    If this is implemented in Gherkin, the runners, like Reqnroll could take a two step approach:

    • first just "convert back" the behavior to the old one (ie throw an error if both specified, and merge them to a single attachment) - this does not seem to be a big effort
    • the second step would be the proper support where the runner details can also be considered. For Reqnroll this is also doable, we anyway serialize all arguments to a single list that we use to invoke the step definition, this serialization would need to be extended a bit.
  4. mpkorstanje commented on May 19, 2026

    @mpkorstanje
    Member

    I remember the first SpecFlow versions supported that before we moved to a shared parser

    Interesting. I wasn't aware of that.

    Additionally, cucumber-rs and (apperently) behave do allow both:

  5. kruczekrobert commented on May 20, 2026

    @kruczekrobert
    Author

    @mpkorstanje @gasparnagy How much information are we waiting for, and when will we be able to move forward with this issue?

  6. gasparnagy commented on May 20, 2026

    @gasparnagy
    Member

    @mpkorstanje @gasparnagy How much information are we waiting for, and when will we be able to move forward with this issue?

    I think we should decide if we would support the table-docstring order as well or just stay with docstring-table. If we are not sure, I would stick to docstring-table.

    I will discuss it today with @sebrose. Maybe he has a good insight for or against this. I will follow up later.

  7. mpkorstanje commented on May 20, 2026

    @mpkorstanje
    Member

    I think we should decide if we would support the table-docstring order as well or just stay with docstring-table. If we are not sure, I would stick to docstring-table.

    I think table-docstring looks better. 😄

    But if we support one, I'd like to support both. Introducing the other order later on would be an equally big change. And there is enough space in Cucumber messages to get the ordering in a backwards compatible manner.

    The ordering info must be optional for backwards compatibility, but can default to a yet to be decided canonical table-docstring or docstring-table if both are present without ordering (bit of an edge case though).

  8. mpkorstanje commented on May 20, 2026

    @mpkorstanje
    Member

    Noting that @davidjgoss isn't currently in favor cucumber/gherkin#592 (comment).

  9. mpkorstanje commented on May 20, 2026

    @mpkorstanje
    Member

    @elchupanebrej, @youtux, @kieran-ryan, @jsa34, both PyTest BDD implementations are using the official Gherkin parser, would you be interested in sharing your perspective?

  10. gasparnagy commented on May 20, 2026

    @gasparnagy
    Member

    We had a chat with Seb, he was also positive about this improvement and haven't seen stuff that speaks against it.

    On the order: we discussed that one too and found that probably the easiest would be to support both orders (otherwise it might be hard to understand from a parser error message what is wrong).

    About the parameter position we considered two options:

    1. keeping the order as it is in the scenario (if docstring is first, the docstring param should come first)
    2. the param order should be fixed (docstring - datatable) regardless of how it is in the scenario

    Originally we had opposite votes (Seb: 2, Me: 1), but I convinced Seb for 1, mainly with the argument that when custom parameter types are used, it might not be clear from the method which one is which so then it is better to stick to the scenario usage order. But I don't feel very strong about this though, so I'm fine if we decide differently.

  11. gasparnagy commented on May 20, 2026

    @gasparnagy
    Member

    Noting that @davidjgoss isn't currently in favor #592 (comment).

    What @davidjgoss says about this feature being focused on API testing is a relevant point. It would be good to see a valid example that is not focused on technical testing.

  12. youtux commented on May 20, 2026

    @youtux

    @elchupanebrej, @youtux, @kieran-ryan, @jsa34, both PyTest BDD implementations are using the official Gherkin parser, would you be interested in sharing your perspective?

    On what? The order of datatable/docstring?

  13. mpkorstanje commented on May 20, 2026

    @mpkorstanje
    Member

    @youtux the whole proposal in general. For example:

    1. Do you think this is an improvement?
    2. Do you think this is an improvement worth adopting?
    3. Do you know use-cases that would benefit from this?

    If you don't particularly mind either way, that's also a valid position too.

  14. kruczekrobert commented on May 20, 2026

    @kruczekrobert
    Author

    Noting that @davidjgoss isn't currently in favor #592 (comment).

    What @davidjgoss says about this feature being focused on API testing is a relevant point. It would be good to see a valid example that is not focused on technical testing.

    This is pure imagination, but you don't have to look far beyond technical testing.

    Scenario: Reviewing an AML alert narrative
      When the alert narrative is reviewed against the escalation matrix
        | trigger                   | threshold        | escalation level |
        | rapid movement of funds    | over 10000 EUR   | enhanced review  |
        | high-risk jurisdiction     | any amount       | enhanced review  |
        | inconsistent income source | repeated pattern | manual review    |
        """
        The customer received three incoming transfers from unrelated accounts
        and moved the funds to a newly added beneficiary within ten minutes.
        The declared source of income is part-time employment.
        """
      Then the alert should be escalated for enhanced review
    
    Scenario: Assessing a loan application explanation
      When the affordability note is assessed against the lending policy
        | risk factor              | acceptable range        | decision impact |
        | debt-to-income ratio     | below 40%               | approve         |
        | recent missed payments   | none in last 12 months  | approve         |
        | unstable income          | present                 | manual review   |
        """
        The applicant reports variable monthly income because most earnings
        come from freelance contracts. Two payments were missed eight months ago,
        but all accounts are now current.
        """
      Then the application should require manual underwriting review
    
    Scenario: Routing a customer complaint
      When the support message is triaged
        | field            | value       |
        | customer tier    | premium     |
        | product area     | billing     |
        | sentiment        | negative    |
        | expected queue   | escalation  |
        """
        I was charged twice for my renewal and nobody has replied
        to my previous message. Please fix this before the end of day.
        """
      Then the message should be routed to the escalation queue
    
    Scenario: Screening a candidate cover letter
      When the application is reviewed
        | criterion              | expected |
        | role                   | store manager |
        | leadership experience  | yes      |
        | customer-facing        | yes      |
        | shortlist              | yes      |
        """
        I have managed retail teams for five years, trained new supervisors,
        and handled customer escalations across three high-volume stores.
        """
      Then the candidate should be shortlisted
    
    Scenario: Scoring a candidate statement
      When the application statement is scored
        | competency            | minimum score | weight |
        | people management     | 3             | 40%    |
        | conflict resolution   | 2             | 30%    |
        | operational planning  | 2             | 30%    |
        """
        I led a team of twelve supervisors, resolved staffing conflicts,
        and planned weekly store operations across three locations.
        """
      Then the candidate should pass the screening
    
  15. kruczekrobert commented on May 23, 2026

    @kruczekrobert
    Author

    I'm back again - does anyone see any reasons why I shouldn't re-upload the pull request as a proposal for review?

  16. elchupanebrej commented on May 25, 2026

    @elchupanebrej

    @elchupanebrej, @youtux, @kieran-ryan, @jsa34, both PyTest BDD implementations are using the official Gherkin parser, would you be interested in sharing your perspective?

    In my opinion, allowing more than one docstring or data table per step could be valuable. It would make it possible to express richer context directly within a step, especially when the step needs more structured input.

    Of course, a similar context can also be provided through Given steps. However, that approach has a trade-off: it often requires additional communication or state sharing between steps.

    My personal approach, which may not be convenient for every project, is to place YAML inside a docstring and use it to pass whatever structured data the step needs.

  17. kruczekrobert commented on May 26, 2026

    @kruczekrobert
    Author

    @elchupanebrej

    Multi docstring/data table per step is a massive change, this issue is more about not blocking what should already be possible

  18. kruczekrobert commented on May 27, 2026

    @kruczekrobert
    Author

    To give this issue some momentum, I'm re-launching the proposal.
    cucumber/gherkin#599

  19. mpkorstanje commented on Jun 2, 2026

    @mpkorstanje
    Member

    Haven't had chance to discuss this internally yet.

    @kruczekrobert If you are going ahead, keep in mind that:

    Originally we had opposite votes (Seb: 2, Me: 1), but I convinced Seb for 1, mainly with the argument that when custom parameter types are used, it might not be clear from the method which one is which so then it is better to stick to the scenario usage order. But I don't feel very strong about this though, so I'm fine if we decide differently.

    Effectively this means that Cucumber messages would need to be changed to include an optional index parameter for the data table and doc string arguments.

  20. kruczekrobert commented on Jun 3, 2026

    @kruczekrobert
    Author

    Discuss, I will make changes.

  21. mpkorstanje commented on Jun 9, 2026

    @mpkorstanje
    Member

    Had a chat internally.

    We can continue, and for that the following has to be done:

    This is effectively a breaking change so it will go into a major release for gherkin and messages. We have another major change in progress, #2232, scheduling wise that one goes out first and independent from this change to avoid stalling that smaller change.

  22. mpkorstanje commented on Jun 9, 2026

    @mpkorstanje
    Member

    @kruczekrobert would you be willing to create a PR for cucumber/messages?

  23. kruczekrobert commented on Jun 19, 2026

    @kruczekrobert
    Author

    @mpkorstanje I have to slow down a bit with my involvement here due to the issues with the individual startup product, but I will come back here if by then you don't post information here that someone has already started it, ok?

  24. self-assigned this
    on Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions