Cucumber-js: Appel d'étapes à partir de définitions d'étape

Créé le 1 juin 2011  ·  31Commentaires  ·  Source: cucumber/cucumber-js

Autoriser l'appel d'étapes à partir des définitions d'étape.

https://github.com/cucumber/cucumber/wiki/Calling-Steps-from-Step-Definitions

Commentaire le plus utile

:-1: :-1: :-1: :-1: :-1:

L'appel d'étapes à partir de stepdefs est l'une de ces fonctionnalités que j'aurais aimé ne jamais ajouter à Cucumber (-Ruby), car il fournit tellement de corde avec laquelle les gens peuvent se pendre. C'est parce que les stepdefs Ruby utilisent des fermetures anonymes que vous ne pouvez pas appeler d'ailleurs (sauf si vous passez par des cerceaux).

Avec JavaScript, c'est une situation différente ; Les définitions d'étape utilisent des fonctions de première classe !

function x_is_something(x, cb) {
  console.log('x', x);
  cb();
}

Given(/x is (.*)/, x_is_something);

Given(/super x/, function(cb) {
  x_is_something(97, cb);
});

Tous les 31 commentaires

Lorsque cela sera implémenté, gardez à l'esprit que nous prévoyons de déprécier tout ce qui n'est pas #steps pour le faire dans cucumber-rb. Voir https://github.com/cucumber/cucumber/issues/68

Merci Matt. -js ne prendra alors en charge que steps().

Je l'aime bien!

Des progrès à ce sujet ? Cela semble être une fonctionnalité assez vitale.

Ceci n'est pas prévu dans le cadre du jalon actuel (0.3). Il _devrait_ faire partie de la 0.4.

@mattwynne Je suppose que nous voulons également prendre en charge step() . Ai-je raison?

@jbpros je suppose. Peut-être pourriez-vous commencer par #step . Il est plus simple à mettre en œuvre car vous invoquez simplement une étape plutôt que d'analyser Gherkin.

Personnellement, je serais heureux d'utiliser un concombre sans cette fonctionnalité, je ne l'utilise jamais et considère que c'est une mauvaise pratique. Je préfère toujours déléguer aux méthodes à la place.

En fin de compte, si nous voulons prendre en charge cela, je préférerais le voir implémenté dans Gherkin, vous avez donc un moyen de définir une macro-étape qui correspond à un tas d'autres étapes de bas niveau. Le concombre reçoit alors simplement l'ordre d'invoquer les niveaux inférieurs et n'a guère besoin de savoir qu'il y a une cartographie en cours. Ce serait ma préférence.

TL; DR : Doit-on vraiment ajouter steps() / step() à Cucumber.js (et -jvm, -ruby 2, etc.) ?

Je suis tout à fait d'accord avec toi Mat. _Malheureusement_, c'est la fonctionnalité la plus recherchée en ce moment sur Cucumber.js.

Si je comprends bien, beaucoup de gens considèrent les définitions d'étape comme des _méthodes_ ou des _fonctions_. De mon point de vue, ce sont des correspondances entre des phrases en langage courant et des morceaux de code de langage de programmation, rien de plus. Cela a de profondes implications sur la façon dont nous traitons ces bêtes.

Du point de vue du programmeur, les définitions d'étape ressemblent cependant à des méthodes. Je le vois comme une faiblesse dans Cucumber_s_ aujourd'hui. Les définitions d'étape ne doivent pas être exposées comme une API mais plutôt comme une carte de traduction explicite, un dictionnaire, pour ainsi dire.

@msassak avait déjà des réflexions intéressantes à ce sujet et je pense qu'il a fait un excellent travail en remodelant ces "mappings" dans Cucumber 2.0.

Je dois dire que je suis réticent à travailler sur ce problème dans Cucumber.js en ce moment. D'un autre côté, je ne veux pas faire de rétention de fonctionnalités uniquement à cause de mes goûts/opinions personnels.

:-1: :-1: :-1: :-1: :-1:

L'appel d'étapes à partir de stepdefs est l'une de ces fonctionnalités que j'aurais aimé ne jamais ajouter à Cucumber (-Ruby), car il fournit tellement de corde avec laquelle les gens peuvent se pendre. C'est parce que les stepdefs Ruby utilisent des fermetures anonymes que vous ne pouvez pas appeler d'ailleurs (sauf si vous passez par des cerceaux).

Avec JavaScript, c'est une situation différente ; Les définitions d'étape utilisent des fonctions de première classe !

function x_is_something(x, cb) {
  console.log('x', x);
  cb();
}

Given(/x is (.*)/, x_is_something);

Given(/super x/, function(cb) {
  x_is_something(97, cb);
});

CLOSED (WONTFIX) :marteau:

Je ne suis pas sûr de comprendre comment step() serait mieux qu'un simple appel de fonction JS ici. N'est-ce pas ce qu'il finirait par faire de toute façon, avec une couche supplémentaire d'indirection (c'est-à-dire une définition d'étape pour faire une requête GET à un utilisateur spécifique qui doit encore être traduite en une fonction JS) ?

J'ai remarqué que vous avez écrit _puisque je n'ai aucun moyen de déclencher un scénario_, vouliez-vous dire étape ou est-ce intentionnellement un scénario (dans ce dernier cas, je peux voir ce que vous essayez de faire, je pense).

Vous pouvez toujours définir des utilisateurs en arrière-plan et les parcourir dans votre étape When .

Feature:
  Background:
    Given a valid user called Simon
    And a valid user called Sarah
    And a valid user called Johnny

  Scenario Outline:
    When each valid user sends a GET request to /search<query>
    Then everyone's request response code is 400
    And everyone's request response body starts with <body>

  Examples:
    | query  | body |
    | ?foo=1 | ...  |

Et oui, cela signifie stocker les réponses aux requêtes dans un tableau et les parcourir. Est-ce si mauvais ?

Eh bien, si vous avez un flux (par exemple un flux de paiement) et que vous souhaitez accéder à la page de confirmation finale, vous pouvez passer par des étapes définies (quelque part) dans les définitions d'étape.
Je suis d'accord que vous pouvez également définir une fonction quelque part et l'invoquer à partir de la définition de l'étape. Mais cela minimise l'effort de le déplacer des étapes à la fonction. Si vous avez décrit un flux quelque part dans BDD, vous n'avez pas besoin de passer plus de temps pour le déplacer vers une bibliothèque séparée, vous pouvez simplement invoquer des définitions d'étape.

Invoquer une seule étape est presque inutile. Je pensais porter ici toutes les fonctionnalités des étapes de ruby. Mais comme ma demande était close, je n'y consacrerais pas de temps.

Merci.

Tellement triste que cela ne résoudra pas, comme le dit @cono : invoquer une seule étape est presque inutile, les cas réels sont des opérations plus complexes.

Cela serait vraiment utile pour créer quelques scénarios à grain fin, puis les autres scénarios répétant ces opérations. Surtout lorsque les étapes sont définies dans plus d'un fichier, dans ce cas, l'alternative de réutilisation des fonctions n'est pas assez facile ni propre.

Hy! J'ai créé une bibliothèque qui fait exactement ce que vous demandez (appelez une étape à partir d'une autre étape), jetez un œil ici : https://github.com/hackhat/cucumberry
Vos commentaires sont les bienvenus !

@hackhat Ça a l'air vraiment cool. J'aime généralement la partie synchronisation de la définition de l'étape. Cucumber-pro est-il uniquement un plugin pour cucumber.js ?

@ jlin412 Je ne sais pas vraiment comment appeler, mais c'est comme une aide pour le concombre. Merci pour vos commentaires

@hackhat Afin de créer une étape de synchronisation, je devrai utiliser la syntaxe : this.addStep(...) ? Aurai-je également besoin d'utiliser selenium-sync au lieu de protractor.js/webdriver.js ?

@ jlin412 , vous ne pouvez utiliser que le plug-in de synchronisation, consultez la documentation pour savoir comment procéder. Je suis sur mobile donc je ne peux pas vous donner les étapes exactes. Si vous pouvez attendre je vous expliquerai mieux vers 23h30 heure Portugal.

@hackhat veuillez changer le nom de votre projet en autre chose. Voir hackhat/concombre-pro#1

@aslakhellesoy A changé le nom en https://github.com/hackhat/cucumberry

@aslakhellesoy Alors comment enchaîner les step calls ? Vous aimez suivre ?

function x_is_something(x, cb) {
  console.log('x', x);
  this.x = x;
  cb();
}
function y_is_something(y, cb) {
  console.log('y', y);
  this.y = y;
  cb();
}

Given(/super z/, function(cb) {
  x_is_something(97, cb);
  y_is_something(8, cb);
});

Cela ne fonctionne pas très bien car x_is_something aurait appelé le rappel avant que y_is_something n'ait la chance de terminer son travail.

Et aussi, si l'étape stocke la variable dans le contexte mondial, elle finira à chaque appel de la fonction, nous devons la lier comme :

Given(/super z/, function(cb) {
  x_is_something.bind(this)(97, cb);
  y_is_something.bind(this)(8, cb);
});

Quelqu'un aurait-il des solutions à ces problèmes ?

Vous devez utiliser async et utiliser la fonction parallèle. De cette façon, vous appelez
le cb principal seulement après que les deux sous-appels soient terminés.
À propos de la liaison, vous pouvez soit l'utiliser avec bind, soit utiliser une variable de fermeture.

Le jeudi 14 mai 2015, 00h15, Yun Jia [email protected] a écrit :

@aslakhellesoy https://github.com/aslakhellesoy Alors comment enchaîner les
appels étape ? Vous aimez suivre ?

fonction x_est_quelque chose(x, cb) {
console.log('x', x);
this.x = x;
cb();
}fonction y_est_quelque chose(y, cb) {
console.log('y', y);
this.y = y;
cb();
}

Étant donné(/super z/, fonction(cb) {
x_is_something(97, cb);
y_est_quelque chose(8, cb);
});

Cela ne fonctionne pas très bien puisque x_is_something aurait appelé le
rappel avant que y_is_something ait la chance de terminer son travail.

Et aussi, si l'étape stocke la variable dans le contexte mondial, elle finira par
chaque fois que nous appelons la fonction, nous devons la lier comme :

Étant donné(/super z/, fonction(cb) {
x_is_something.bind(this)(97, cb);
y_is_something.bind(this)(8, cb);
});

Quelqu'un aurait-il des solutions à ces problèmes ?


Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/cucumber/cucumber-js/issues/11#issuecomment -101845619
.

+1, cela devrait faire partie de la lib.

Ce que @mattwynne a suggéré (ajoutez une fonctionnalité gherkin qui prend en charge la réutilisation du code):

    When a
    Then x
    When b
    Then y

---------------------------------

    Define x
        Then p
        And y
        And q

---------------------------------

    Step a
        ...
    Step b
        ...
    Step p
        ...
    Step q
        ...
    Step y
        ...

---------------------------------

Ce que @aslakhellesoy a suggéré (extraction du code dupliqué en fonctions js):

    When a
    Then x
    When b
    Then y

---------------------------------

    Step a
        ...
    Step b
        ...
    Step x
        ...
        Call f(p)
        ...
    Step y
        Call f(p)

---------------------------------

    Function f(p)
        ...

---------------------------------

Appel d'étapes à partir de définitions d'étape

    When a
    Then x
    When b
    Then y

---------------------------------

    Step a
        ...
    Step b
        ...
    Step x
        ...
        Call y(p)
        ...
    Step y
        ...

---------------------------------

Je ne comprends toujours pas pourquoi avons-nous besoin d'un autre niveau d'abstraction inutile, avez-vous une explication ?

@yunjia c'est la théorie de base du rappel :

Given(/super z/, function(cb) {
  x_is_something(97, function () {
    y_is_something(8, cb);
  });
});

En ce qui concerne le problème de liaison, vous devez définir ces fonctions comme des méthodes sur votre World :

function World(callback) {
    this.x_is_something = function (x, callback) {
      this.x = ...
    };

    this.y_is_something = function (y, callback) {
      this.y = ...
    };

    callback(); // tell Cucumber we're finished and to use 'this' as the world instance
  };
}
module.exports.World = World;

Ensuite, dans vos définitions d'étape :

Given(/super z/, function(cb) {
  var self = this;
  self.x_is_something(97, function () {
    self.y_is_something(8, cb);
  });
});

Veuillez également noter que les définitions d'étapes synchrones sont prises en charge par Cucumber.js 0.5+ :

Given(/super z/, function() {
  this.x_is_something(97);
  this.y_is_something(8);
});

Les définitions d'étape @ inf3rno sont une fine couche de traduction entre l'anglais simple et le code JS. En autorisant l'appel d'étapes à partir d'étapes, nous rendons cette couche plus épaisse. Les marches deviennent couplées les unes aux autres, ce qui les rend extrêmement difficiles à entretenir.

Cela encourage également les gens à utiliser Gherkin comme langage de script, ce qui n'est pas du tout le cas.

@ inf3rno si vous souhaitez réutiliser le code dans les stepdefs, déplacez le corps du stepdef vers une fonction javascript normale et réutilisez-la.

@jbpros

Cela encourage également les gens à utiliser Gherkin comme langage de script, ce qui n'est pas du tout le cas.

Pouvez-vous élaborer svp. Je ne comprends pas quel est le lien, car les définitions d'étape ne sont pas en cornichon, seul le modèle de texte est similaire.

@ inf3rno si vous pouvez appeler des étapes à partir d'étapes, vous revenez dans "Gherkinland": les noms d'étape doivent être analysés, mis en correspondance avec les définitions d'étape qui peuvent être exécutées. Vous écrivez essentiellement des scripts gherkin dans gherkin (ils sont cachés dans les définitions d'étape JS, mais c'est un détail qui aggrave encore les choses à partir d'un POV de maintenance).

@aslakhellesoy @jbpros L'idée est que les étapes doivent être un type algébrique composable, ce qui n'est pas le cas.

@jbpros , je vais utiliser votre solution car je suis habitué à programmer en Java et à ne pas me soucier des promesses :)

Quelqu'un a-t-il déjà proposé une extension au concombre pour définir les étapes en termes d'autres étapes ? Quelque chose comme

Understand I log in to {site} as {user}, {pass}
    I visit {site}
    I log in as {user}, {pass}

Je me demande s'il s'agit d'une extension utile pour mieux décrire les longs trajets des utilisateurs dans le système et je préférerais travailler avec n'importe quel art antérieur.

Ce fil a été automatiquement verrouillé puisqu'il n'y a eu aucune activité récente après sa fermeture. Veuillez ouvrir un nouveau problème pour les bogues associés.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

lamartire picture lamartire  ·  6Commentaires

NoNameProvided picture NoNameProvided  ·  5Commentaires

protoman92 picture protoman92  ·  3Commentaires

edgarechm picture edgarechm  ·  5Commentaires

kozhevnikov picture kozhevnikov  ·  6Commentaires