badeball/cypress-cucumber-preprocessor

Deprecate `And` and `But` functions for step definitions

aslakhellesoy opened this issue ยท 19 comments

Current behavior

Step definitions can be defined with Given, When, Then, And and But - see

export {
defineStep as Given,
defineStep as When,
defineStep as Then,
defineStep as And,
defineStep as But,
};

Desired behavior

Step definitions can be defined with Given, When and Then. This is what Cucumber.js does: https://github.com/cucumber/cucumber-js/blob/01368ca6e3c9b097a7a88bb72de5beeddb4a0326/src/index.ts#L45-L51

Here is an example illustrating why:

Feature: banking
  Scenario: Overdraft
    Given I have an overdraft limit of 50
    And I have a balance of 300
    When I withdraw 330
    Then my balance should be -30

With the currentl API, people might implement step definitions this way:

Given('I have an overdraft limit of {int}')
And('I have a balance of {int}')

But what if I add a new scenario:

  Scenario: No overdraft
    Given I have a balance of 300
    When I withdraw 330
    Then my balance should be 300
    And I should be told my overdraft limit is 0

It no longer makes sense that the step defnition is defined with And.

The And and But keywords only belong in Gherkin. Step definitions are reusable in any order, and should therefore not use And and But.

Checklist

  • I've read the FAQ.
  • I've read Instructions for logging issues.
  • I'm not using cypress-cucumber-preprocessor@4.3.1 (package name has changed and it is no longer the most recent version, see #689).

Fixed with v13.0.0.

Wow. This is a huge breaking change, isn't it?

Could you please explain, why And/But steps are not reusable? And/But after Then come with a should that And/But after Given or When do not. As in your examples. Thus they cannot be mixed up. The syntax is clear. Without should it is an action (clicks etc), with should it is a check (cy.contains() etc).

I also have those reusable steps in different files like And-after-Then.ts and And-after-When.ts.

Wow. This is a huge breaking change, isn't it?

You thinking this make me suspect that you're misunderstanding the implications of this change. This only affects the method you use for defining steps, aka Given(..), When(..) and Then(..). With this change, you would still use the keywords And and But in the gherkin / feature files.

However, with a file a

Feature:
  Scenario:
    Given something
    And another thing

.. then another thing is implicitly considered to be a given-step and it should be declared using Given(..).

Thus, the only thing I expect anyone to change is swapping out calls to And(..) and But(..) with the more appropriate methods.

Ah, now I understand. But in German this does not work. The sentence structure after Dann (German for Then) is different than after Und (German for And).

But I already found a workaround. I just use defineStep instead.

import { Before, defineStep, Given, Step, Then, When } from '@badeball/cypress-cucumber-preprocessor';
export const Und = function (description: string | RegExp, implementation: IStepDefinitionBody<unknown[]>) {
    return defineStep(toCucumberExpOrRegExp(description), implementation);
};
export const Aber = function (description: string | RegExp, implementation: IStepDefinitionBody<unknown[]>) {
    return defineStep(toCucumberExpOrRegExp(description), implementation);
};

FYI, defineStep() is going away from cucumber-js (cucumber/cucumber-js#2043) and this will eventually be reflected in this lib as well.

FYI, defineStep() is going away from cucumber-js (cucumber/cucumber-js#2043) and this will eventually be reflected in this lib as well.

I guess this means that my workaround will get quite ugly then, e.โ€ฏg. mapping everything to When or so. But thanks anyway!

Ah, now I understand. But in German this does not work. The sentence structure after Dann (German for Then) is different than after Und (German for And).

@iomedico-beyer can you give me a concrete example of a sentence/step that would be different with Dann and Und? I'm one of the maintainers of Cucumber and understanding this would be helpful.

@iomedico-beyer can you give me a concrete example of a sentence/step that would be different with Dann and Und? I'm one of the maintainers of Cucumber and understanding this would be helpful.

Sure :)

Then there should be an "OK" button
And there should be a "Cancel" button

is in German

Dann sollte es einen "OK"-Button geben
Und es sollte einen "Cancel"-Button geben

Subject and predicate swap places.

A linguistic alternative would be to add a "dann " to the Und:

Dann sollte es einen "OK"-Button geben
Und dann sollte es einen "Cancel"-Button geben

However, that sounds more than a little odd.
(And I would have to do a little transformation to the strings, removing the "dann " prefix.)

woulnd't it be better to support defining steps as both 'When' and 'And' like other bdd frameworks?

woulnd't it be better to support defining steps as both 'When' and 'And' like other bdd frameworks?

I guess the idea is that When is more descriptive/clear than And, because And can also be used after Given and Then.

Also the framework can narrow its matching to look e.โ€ฏg. only in When declarations for a specific And test step (that comes after a When test step) โ€“ although I'm not sure if this is particularly helpful (Given and When seem semantically similar enough for me for And to match both).

Given and When seem semantically similar enough for me for And to match both

Recommended reading: A little tense

Recommended reading: A little tense

Interesting! Here is a typical test of mine translated into English and trying to follow that guide:

Given I went to the "Select analysis" page
And I clicked on "Create new analysis"
When I click on "Go back"
And I click on "Discard config"
Then I should be on the "Select analysis" page again

So for the clicking steps I would have to implement the "clicked" variant with the Given command and the "click" variant with the When command. I'm not quite sure if that's a good idea. What do you think?

Sorry if that's awfully off topic!

I agree this was hastily implemented. This feels a bit too opinionated from the author.

what a shame, AND/OR is widely used in Gherkin sintax and should reflect in the tests.

And(..) and Or(..) has afaik never been exported members in cucumber-js (ref. cucumber/cucumber-js#1118 and cucumber/cucumber-js#1615) and this has not changed recently. The only thing that has changed recently, is the feature alignment between this preprocessor and cucumber-js, where this preprocessor aims for feature identicality. Exporting And and But was simply a mistake and did not correctly reflect the experience found in cucumber-js.

Furthermore, cucumber-js is not developed here. Changes there can trickle down and be reflected here, but not the other way around, because this preprocessor simply isn't associated with Cucumber in any way. If you think it's a mistake that cucumber-js doesn't export And(..) and But(..), then I suggest you take it up somewhere more appropriate.

I want to highlight the following comment from Mar 16, 2021: cucumber/cucumber-js#1615 (comment)

We're never going to add And and But as step definition methods.

@badeball ,
Thanks for the update, so we can have in our feature file still the And or But.
However in our code we should state Given, When or Then

E.g.
Feature file:

Given I open website
Then my website is shown
And the data is shown

Code:

Given('I open website', () => {
  do stuff;
});

Then('my website is shown', () => {
  do stuff;
});

Then('the data is shown', () => {
  do stuff;
});

Making it also easier to understand if the command is a given, when or then command.

Ah, now I understand. But in German this does not work. The sentence structure after Dann (German for Then) is different than after Und (German for And).

But I already found a workaround. I just use defineStep instead.

import { Before, defineStep, Given, Step, Then, When } from '@badeball/cypress-cucumber-preprocessor';
export const Und = function (description: string | RegExp, implementation: IStepDefinitionBody<unknown[]>) {
    return defineStep(toCucumberExpOrRegExp(description), implementation);
};
export const Aber = function (description: string | RegExp, implementation: IStepDefinitionBody<unknown[]>) {
    return defineStep(toCucumberExpOrRegExp(description), implementation);
};

are you still using this method? where did u place this, in your step definition fille?

are you still using this method?

No, in the end we switched to English.

where did u place this, in your step definition fille?

No, somewhere in cypress/support/.