Cucumber-js: Aufrufen von Schritten aus Schrittdefinitionen

Erstellt am 1. Juni 2011  ·  31Kommentare  ·  Quelle: cucumber/cucumber-js

Hilfreichster Kommentar

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

Das Aufrufen von Schritten aus stepdefs ist eine dieser Funktionen, von denen ich wünschte, ich hätte sie nie zu Cucumber(-Ruby) hinzugefügt, weil es so viele Seile bietet, an denen sich die Leute aufhängen können. Es entstand, weil Ruby-Stepdefs anonyme Closures verwenden, die Sie nicht von woanders aufrufen können (es sei denn, Sie gehen durch Reifen).

Bei JavaScript ist es eine andere Situation; Schrittdefinitionen verwenden erstklassige Funktionen!

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);
});

Alle 31 Kommentare

Wenn dies implementiert ist, denken Sie daran, dass wir planen, alles andere als #steps dafür in cucumber-rb zu verwerfen. Siehe https://github.com/cucumber/cucumber/issues/68

Danke Matt. -js unterstützt dann nur steps().

Ich mag das!

Irgendwelche Fortschritte dabei? Scheint ein ziemlich wichtiges Feature zu sein.

Dies ist im Rahmen des aktuellen Meilensteins (0.3) nicht vorgesehen. Es _sollte_ Teil von 0.4 sein.

@mattwynne Ich schätze, wir wollen auch step() unterstützen. Hab ich recht?

@jbpros denke ich. Vielleicht könnten Sie mit #step beginnen. Es ist einfacher zu implementieren, da Sie nur einen Schritt aufrufen, anstatt Gherkin zu analysieren.

Ich persönlich würde gerne eine Gurke ohne dieses Feature verwenden, ich benutze es nie und halte es für eine schlechte Übung. Ich ziehe es immer vor, stattdessen an Methoden zu delegieren.

Wenn wir dies unterstützen, würde ich es letztendlich vorziehen, es in Gherkin implementiert zu sehen, sodass Sie eine Möglichkeit haben, einen Makroschritt zu definieren, der einer Reihe anderer Schritte auf niedriger Ebene zugeordnet ist. Cucumber wird dann nur angewiesen, die untergeordneten Ebenen aufzurufen, und muss kaum wissen, dass eine Zuordnung stattfindet. Das wäre meine Präferenz.

TL;DR: Sollten wir wirklich steps() / step() zu Cucumber.js (und -jvm, -ruby 2 usw.) hinzufügen?

Ich stimme dir vollkommen zu Matt. _Leider_ ist dies derzeit das meistgesuchte Feature von Cucumber.js.

So wie ich es verstehe, betrachten viele Leute Schrittdefinitionen als _Methoden_ oder _Funktionen_. So wie ich es sehe, sind sie Zuordnungen zwischen einigen Sätzen in fließender Sprache und Teilen des Programmiersprachencodes, nicht mehr. Dies hat tiefgreifende Auswirkungen darauf, wie wir diese Bestien behandeln.

Aus der Sicht eines Programmierers sehen Step-Definitionen jedoch genauso aus wie Methoden. Ich sehe es heute als eine Schwäche von Cucumber_s_. Schrittdefinitionen sollten nicht als API exponiert werden, sondern eher als explizite Übersetzungskarte, sozusagen als Wörterbuch.

@msassak hatte bereits interessante Gedanken dazu und ich denke, er hat großartige Arbeit geleistet, indem er diese "Mappings" in Cucumber 2.0 neu modelliert hat.

Ich muss sagen, dass ich derzeit nur ungern an genau diesem Problem in Cucumber.js arbeite. Auf der anderen Seite möchte ich die Feature-Retention nicht nur wegen meiner persönlichen Vorlieben/Meinungen machen.

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

Das Aufrufen von Schritten aus stepdefs ist eine dieser Funktionen, von denen ich wünschte, ich hätte sie nie zu Cucumber(-Ruby) hinzugefügt, weil es so viele Seile bietet, an denen sich die Leute aufhängen können. Es entstand, weil Ruby-Stepdefs anonyme Closures verwenden, die Sie nicht von woanders aufrufen können (es sei denn, Sie gehen durch Reifen).

Bei JavaScript ist es eine andere Situation; Schrittdefinitionen verwenden erstklassige Funktionen!

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) :hammer:

Ich bin mir nicht sicher, ob ich verstehe, wie step() hier besser wäre als ein einfacher JS-Funktionsaufruf. Ist es nicht sowieso das, was es tun würde, mit einer zusätzlichen Indirektionsschicht (dh einer Schrittdefinition, um eine GET-Anforderung an einen bestimmten Benutzer zu stellen, die noch in eine JS-Funktion übersetzt werden muss)?

Mir ist aufgefallen, dass Sie geschrieben haben _da ich keine Möglichkeit habe, ein Szenario auszulösen_, meinten Sie Schritt oder ist das absichtliches Szenario (im letzteren Fall kann ich sehen, was Sie versuchen, denke ich).

Sie können Benutzer weiterhin im Hintergrund definieren und in Ihrem Schritt When über sie iterieren.

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 | ...  |

Und ja, das bedeutet, dass Anfragenantworten in einem Array gespeichert und darüber iteriert werden. Ist es so schlimm?

Nun, wenn Sie einen Flow haben (z. B. Checkout-Flow) und zur letzten Bestätigungsseite gelangen möchten, können Sie die Schritte durchlaufen, die (irgendwo) in den Schrittdefinitionen definiert sind.
Ich stimme zu, dass Sie auch irgendwo eine Funktion definieren und sie aus der Schrittdefinition aufrufen können. Aber es minimiert den Aufwand, es von den Schritten zur Funktion zu bewegen. Wenn Sie irgendwo in BDD einen Ablauf beschrieben haben, müssen Sie keine zusätzliche Zeit aufwenden, um ihn in eine separate Bibliothek zu verschieben, Sie können einfach Schrittdefinitionen aufrufen.

Das Aufrufen eines einzelnen Schrittes ist fast nutzlos. Ich dachte daran, alle Schritt(e)-Funktionalitäten von Ruby hierher zu portieren. Aber da meine Anfrage geschlossen war, würde ich keine Zeit dafür aufwenden.

Danke.

So traurig, dass dies nicht behoben wird, wie @cono sagt: Das Aufrufen eines einzelnen Schritts ist fast nutzlos, echte Fälle sind komplexere Operationen.

Dies wäre wirklich hilfreich, um einige feinkörnige Szenarien zu erstellen und dann die anderen Szenarien, die diese Vorgänge wiederholen. Besonders wenn die Schritte in mehr als einer Datei definiert sind, ist die Alternative zur Wiederverwendung von Funktionen in diesem Fall weder ganz einfach noch sauber.

Hy! Ich habe eine Bibliothek erstellt, die genau das tut, was Sie fragen (rufen Sie einen Schritt von einem anderen Schritt auf), werfen Sie einen Blick hier: https://github.com/hackhat/cucumberry
Feedback ist willkommen!

@hackhat Es sieht wirklich cool aus. Normalerweise mag ich den Synchronisierungsteil der Schrittdefinition. Ist cucumber-pro nur ein Plugin für cucumber.js?

@jlin412 Ich weiß nicht wirklich, wie ich anrufen soll, ist aber wie ein Helfer für Gurke. Danke für die Rückmeldung

@hackhat Um einen Synchronisierungsschritt zu erstellen, muss ich folgende Syntax verwenden: this.addStep(...)? Muss ich anstelle von protractor.js/webdriver.js auch selenium-sync verwenden?

@ jlin412 Sie können nur das Synchronisierungs-Plug-In verwenden, sehen Sie sich die Dokumentation an, wie es geht. Ich bin auf dem Handy, daher kann ich Ihnen nicht die genauen Schritte geben. Wenn Sie warten können, werde ich es gegen 23:30 Uhr in Portugal besser erklären.

@hackhat bitte ändern Sie den Namen Ihres Projekts in etwas anderes. Siehe hackhat/gurke-pro#1

@aslakhellesoy Namen geändert in https://github.com/hackhat/cucumberry

@aslakhellesoy Wie verkettet man dann die Schrittaufrufe? Wie folgt?

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);
});

Das funktioniert nicht sehr gut, da x_is_something den Callback aufgerufen hätte, bevor y_is_something die Chance hat, seine Arbeit zu beenden.

Und wenn step Variablen im Weltkontext speichert, wird es jedes Mal enden, wenn die Funktion aufgerufen wird, wir müssen sie wie folgt binden:

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

Hatte jemand Lösungen für diese Probleme?

Sie müssen async verwenden und die parallele Funktion verwenden. Auf diese Weise rufen Sie an
das Haupt-CB erst, nachdem beide Sub-Anrufe beendet sind.
Über die Bindung können Sie dies entweder mit bind verwenden oder eine Abschlussvariable verwenden.

Am Do, 14. Mai 2015, 00:15 Uhr schrieb Yun Jia [email protected] :

@aslakhellesoy https://github.com/aslakhellesoy Dann wie man die verkettet
Schritt fordert? Wie folgt?

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

Gegeben(/super z/, Funktion(cb) {
x_ist_etwas(97, cb);
y_ist_etwas(8, cb);
});

Das funktioniert nicht sehr gut, da x_is_something die aufgerufen hätte
Rückruf, bevor y_is_something die Chance hat, seine Arbeit zu beenden.

Und wenn step Variable im Weltkontext speichert, wird es enden
Jedes Mal, wenn Sie die Funktion aufrufen, müssen wir sie wie folgt binden:

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

Hatte jemand Lösungen für diese Probleme?


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/cucumber/cucumber-js/issues/11#issuecomment -101845619
.

+1, dies sollte Teil der Bibliothek sein.

Was @mattwynne vorgeschlagen hat (fügen Sie eine Gurkenfunktion hinzu, die die Wiederverwendbarkeit von Code unterstützt):

    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
        ...

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

Was @aslakhellesoy vorgeschlagen hat (Extrahieren des duplizierten Codes in js-Funktionen):

    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)
        ...

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

Aufrufen von Schritten aus Schrittdefinitionen

    When a
    Then x
    When b
    Then y

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

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

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

Ich verstehe immer noch nicht, warum wir eine weitere unnötige Abstraktionsebene brauchen, haben Sie eine Erklärung?

@yunjia das ist die grundlegende Callback-Theorie:

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

Was die Bindung betrifft, sollten Sie diese Funktionen als Methoden in Ihrer Welt definieren:

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;

Dann in Ihren Schrittdefinitionen:

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

Beachten Sie außerdem, dass synchrone Schrittdefinitionen von Cucumber.js 0.5+ unterstützt werden:

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

@inf3rno- Schrittdefinitionen sind eine dünne Übersetzungsschicht zwischen einfachem Englisch und JS-Code. Indem wir das Aufrufen von Schritten aus Schritten zulassen, machen wir diese Schicht dicker. Stufen werden miteinander gekoppelt, was ihre Wartung extrem schwierig macht.

Es ermutigt die Leute auch, Gherkin als Skriptsprache zu verwenden, was es überhaupt nicht ist.

@inf3rno Wenn Sie Code in stepdefs wiederverwenden möchten, verschieben Sie den Hauptteil der stepdef in eine reguläre Javascript-Funktion und verwenden Sie diese wieder.

@jbpros

Es ermutigt die Leute auch, Gherkin als Skriptsprache zu verwenden, was es überhaupt nicht ist.

Können Sie bitte näher darauf eingehen? Ich verstehe nicht, was die Verbindung ist, da Schrittdefinitionen nicht in Gurke sind, nur das Textmuster, was ähnlich ist.

@inf3rno Wenn Sie Schritte aus Schritten aufrufen können, springen Sie wieder zurück in "Gherkinland": Die Schrittnamen müssen analysiert und mit Schrittdefinitionen abgeglichen werden, die ausgeführt werden können. Sie schreiben im Grunde Gherkin-Skripte in Gherkin (sie sind in JS-Schrittdefinitionen versteckt, aber das ist ein Detail, das es aus einem Wartungs-POV noch schlimmer macht).

@aslakhellesoy @jbpros Die Idee ist, dass Schritte ein algebraischer, zusammensetzbarer Typ sein sollten, was es nicht ist.

@jbpros , ich werde Ihre Lösung verwenden, da ich es gewohnt bin, in Java zu programmieren und mir keine Gedanken über Versprechen zu machen :)

Hat sich jemals jemand eine Erweiterung für Gurke ausgedacht, um Schritte in Bezug auf andere Schritte zu definieren? Etwas wie

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

Ich überlege, ob dies eine nützliche Erweiterung ist, um lange Benutzerreisen durch das System besser zu beschreiben, und würde es vorziehen, mit jedem Stand der Technik zu arbeiten.

Dieser Thread wurde automatisch gesperrt, da es nach seiner Schließung keine Aktivitäten mehr gegeben hat. Bitte öffnen Sie ein neues Problem für verwandte Fehler.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen